[TIL] 내배캠 8일차

코딩쟝이·2023년 9월 13일
0

내배캠 TIL

목록 보기
8/63

8일차에는 자바스크립트 핸드북 파트3에서 배운내용을 정리했다.

배열

순서가 있는 컬렉션을 저장할때 쓰는 자료구조이다.
배열을 만들때는 아래 문법처럼 대괄호나, 생성자함수(new)를 통해 선언한다.

let arr = new Array();
let arr = [];

배열에 활용법은 다음과 같다.

let fruits = ["사과", "오렌지", "자두"]; // 배열 선언

let fruits = [
  "사과",
  "오렌지",
  "자두",
]; // 배열 마지막 요소는 쉼표(trailing)를 넣어 추가나 삭제가 편하게 할 수도 있다.

// 배열안의 들은 값은 요소라 부르며, 인덱스는 요소의 순서를 뜻하는데 0부터 시작된다.
alert( fruits[0] ); // 사과
alert( fruits[1] ); // 오렌지
alert( fruits[2] ); // 자두

// 값 수정하기
fruits[2] = '배'; // 배열이 ["사과", "오렌지", "배"]로 바뀜

// 값 추가하기
fruits[3] = '레몬'; // 배열이 ["사과", "오렌지", "배", "레몬"]으로 바뀜

length배열의 크기를 나타내준다.

let fruits = ["사과", "오렌지", "자두"];

alert( fruits.length ); // 3

배열에는 다양한 자료형이 들어갈 수 있으며 제약이 없다.

// 요소에 여러 가지 자료형이 섞여 있습니다.
let arr = [ '사과', { name: '이보라' }, true, function() { alert('안녕하세요.'); } ];

// 인덱스가 1인 요소(객체)의 name 프로퍼티를 출력합니다.
alert( arr[1].name ); // 이보라

// 인덱스가 3인 요소(함수)를 실행합니다.
arr[3](); // 안녕하세요.

큐(Queue)에서 쓰이는 연산

큐(queue)는 배열을 사용해 만들 수 있는 대표적인 자료구조로, 배열과 마찬가지로 순서가 있는 컬렉션을 저장하는 데 사용한다. 큐를 사용하면 먼저 집어넣은 요소가 먼저 나오기 때문에 큐는 선입선출(First-In-First-Out, FIFO) 자료구조라고 부른다.
큐에서 사용하는 주요 연산은 아래와 같다.

  • push: 맨 끝에 요소를 추가합니다.
  • shift: 제일 앞 요소를 꺼내 제거한 후 남아있는 요소들을 앞으로 밀어줍니다. 이렇게 하면 두 번째 요소가 첫 번째 요소가 됩니다.
// shift
let fruits = ["사과", "오렌지", "배"];
alert( fruits.shift() ); // 배열에서 "사과"를 제거하고 제거된 요소를 얼럿창에 띄웁니다.
alert( fruits ); // 오렌지,배

// unshift
let fruits = ["오렌지", "배"];
fruits.unshift('사과');
alert( fruits ); // 사과,오렌지,배

스택(stack)에서 쓰이는 연산

배열은 큐 이외에 스택(stack)이라 불리는 자료구조를 구현할 때도 쓰인다. 스택을 사용하면 가장 나중에 집어넣은 요소가 먼저 나오는 후입선출(Last-In-First-Out, LIFO) 자료구조를 갖는다.
스택에서 사용하는 연산은 아래와 같다.

  • push: 요소를 스택 끝에 집어넣는다.
  • pop: 스택 끝 요소를 추출합니다.
// pop
let fruits = ["사과", "오렌지", "배"];
alert( fruits.pop() ); // 배열에서 "배"를 제거하고 제거된 요소를 얼럿창에 띄웁니다.
alert( fruits ); // 사과,오렌지

//push
let fruits = ["사과", "오렌지"];
fruits.push("배");
alert( fruits ); // 사과,오렌지,배

배열의 잘못된 예시

let fruits = []; // 빈 배열을 하나 만듭니다.

fruits[99999] = 5; // 배열의 길이보다 훨씬 큰 숫자를 사용해 프로퍼티를 만듭니다.

fruits.age = 25; // 임의의 이름을 사용해 프로퍼티를 만듭니다.

배열은 객체이므로 위에 예시처럼 원하는 프로퍼티를 추가해도 문제가 발생되지 않지만, 이렇게 코드를 작성하면 자바스크립트 엔진이 배열을 일반 객체처럼 다루게 되어 배열을 다룰 때만 적용되는 최적화 기법이 동작하지 않아 배열 특유의 이점이 사라진다.
배열은 순서가 있는 자료를 저장하는 용도로 만들어진 특수한 자료구조이다. 배열 내장 메서드들은 이런 용도에 맞게 만들어졌기 때문에 자바스크립트 엔진은 이런 특성을 고려하여 배열을 신중하게 조정하고, 처리하므로 배열을 사용할 땐 이런 목적에 맞게 사용해야 한다. 임의의 키를 사용해야 한다면 배열보단 일반 객체 {}가 적합한 자료구조일 확률이 높다.
잘못된 예시는 다음과 같다.

  • arr.test = 5 같이 숫자가 아닌 값을 프로퍼티 키로 사용하는 경우
  • arr[0]과 arr[1000]만 추가하고 그사이에 아무런 요소도 없는 경우
  • arr[1000], arr[999]같이 요소를 역순으로 채우는 경우

배열 연산의 성능

push와 pop은 빠르지만 shift와 unshift는 느리다.
shift와 unshift를 사용하면 배열에 요소가 많으면 요소가 이동하는 데 걸리는 시간이 길고 메모리 관련 연산도 많아지지만,
pop 메서드는 요소를 옮기지 않으므로 각 요소는 기존 인덱스를 그대로 유지하기 때문에 배열 끝에 무언가를 해주는 메서드의 실행 속도가 빠르며,
push 메서드를 쓸 때도 유사한 동작이 일어나므로 속도가 빠르다.

배열 반복문

// for문
let arr = ["사과", "오렌지", "배"];

for (let i = 0; i < arr.length; i++) {
  alert( arr[i] );
}

// for..of문
let fruits = ["사과", "오렌지", "자두"];
// 배열 요소를 대상으로 반복 작업을 수행합니다.
for (let fruit of fruits) {
  alert( fruit );
}

다차원 배열

배열은 배열의 요소가 될 수 있는데 이런 배열을 다차원배열(multidimensional array)이라고 부르며, 행렬을 저장하는 용도로 쓰인다.

let matrix = [
  [1, 2, 3],
  [4, 5, 6],
  [7, 8, 9]
];

alert( matrix[1][1] ); // 5, 중심에 있는 요소

toString

배열엔 toString 메서드가 구현되어 있어 이를 호출하면 요소를 쉼표로 구분한 문자열이 반환된다.

let arr = [1, 2, 3];

alert( arr ); // 1,2,3
alert( String(arr) === '1,2,3' ); // true

그 외 메서드

arr.splice(start)로 요소를 자유자재로 다룰 수 있으며 이 메서드를 사용하면 요소 추가, 삭제, 교체가 모두 가능하다.

// 문법
arr.splice(index[, deleteCount, elem1, ..., elemN])

// 예시
let arr = ["I", "study", "JavaScript"];

arr.splice(1, 1); // 인덱스 1부터 요소 한 개를 제거

alert( arr ); // ["I", "JavaScript"]

arr.slice 이용한 문자열 자르기도 가능하다.이 메서드는 "start" 인덱스부터 ("end"를 제외한) "end" 인덱스까지의 요소를 복사한 새로운 배열을 반환한다. start와 end는 둘 다 음수일 수 있는데 이땐, 배열 끝에서부터의 요소 개수를 의미한다.
arr.slice()는 인수를 하나도 넘기지 않고 호출하여 arr의 복사본을 만들 수 있다. 이런 방식은 기존의 배열을 건드리지 않으면서 배열을 조작해 새로운 배열을 만들고자 할 때 자주 사용된다.

// 문법
arr.slice([start], [end]) 

// 예시
let arr = ["t", "e", "s", "t"];

alert( arr.slice(1, 3) ); // e,s (인덱스가 1인 요소부터 인덱스가 3인 요소까지를 복사(인덱스가 3인 요소는 제외))

alert( arr.slice(-2) ); // s,t (인덱스가 -2인 요소부터 제일 끝 요소까지를 복사)

arr.concat은 기존 배열의 요소를 사용해 새로운 배열을 만들거나 기존 배열에 요소를 추가하고자 할 때 사용할 수 있다.

// 문법
arr.concat(arg1, arg2...)

// 예시
let arr = [1, 2];

// arr의 요소 모두와 [3,4]의 요소 모두를 한데 모은 새로운 배열이 만들어집니다.
alert( arr.concat([3, 4]) ); // 1,2,3,4

// arr의 요소 모두와 [3,4]의 요소 모두, [5,6]의 요소 모두를 모은 새로운 배열이 만들어집니다.
alert( arr.concat([3, 4], [5, 6]) ); // 1,2,3,4,5,6

// arr의 요소 모두와 [3,4]의 요소 모두, 5와 6을 한데 모은 새로운 배열이 만들어집니다.
alert( arr.concat([3, 4], 5, 6) ); // 1,2,3,4,5,6

// 객체로 넘어올때는 분해되지 않고 통으로 넘어온다.
let arr = [1, 2];

let arrayLike = {
  0: "something",
  length: 1
};

alert( arr.concat(arrayLike) ); // 1,2,[object Object]

// Symbol.isConcatSpreadable 프로퍼티를 추가하여 객체를 배열로 취급할 수 있게 한다.
let arr = [1, 2];

let arrayLike = {
  0: "something",
  1: "else",
  [Symbol.isConcatSpreadable]: true,
  length: 2
};

alert( arr.concat(arrayLike) ); // 1,2,something,else

forEach문을 통해 배열을 반복시킬수도 있다.

arr.forEach(function(item, index, array) {
  // 요소에 무언가를 할 수 있습니다.
});

배열을 탐색하는 메서드는 다음과 같다.

  • arr.indexOf(item, from)는 인덱스 from부터 시작해 item(요소)을 찾는다. 요소를 발견하면 해당 요소의 인덱스를 반환하고, 발견하지 못했으면 -1을 반환한다.
  • arr.lastIndexOf(item, from)는 위 메서드와 동일한 기능을 하는데, 검색을 끝에서부터 시작한다는 점이 다르다.
  • arr.includes(item, from)는 인덱스 from부터 시작해 item이 있는지를 검색하는데, 해당하는 요소를 발견하면 true를 반환한다.
let arr = [1, 0, false];

alert( arr.indexOf(0) ); // 1
alert( arr.indexOf(false) ); // 2
alert( arr.indexOf(null) ); // -1

alert( arr.includes(1) ); // true

// includes는 NaN도 처리가 가능하다.
const arr = [NaN];
alert( arr.indexOf(NaN) ); // -1 (완전 항등 비교 === 는 NaN엔 동작하지 않으므로 0이 출력되지 않습니다.)
alert( arr.includes(NaN) );// true (NaN의 여부를 확인하였습니다.)

특정조건에 부합하는 객체를 배열내에서 찾기위한 메서드는 findfindIndex가 있다.

// 문법
let result = arr.find(function(item, index, array) {
  // true가 반환되면 반복이 멈추고 해당 요소를 반환합니다.
  // 조건에 해당하는 요소가 없으면 undefined를 반환합니다.
});

// 예시
let users = [
  {id: 1, name: "John"},
  {id: 2, name: "Pete"},
  {id: 3, name: "Mary"}
];

let user = users.find(item => item.id == 1);

alert(user.name); // John
  • item: 함수를 호출할 요소
  • index: 요소의 인덱스
  • array: 배열 자기 자신
    arr.findIndex는 find와 동일한 일을 하나, 조건에 맞는 요소를 반환하는 대신 해당 요소의 인덱스를 반환한다는 점이 다르다. 조건에 맞는 요소가 없으면 -1이 반환된다.

배열을 변형시키거나 요소를 재 정렬해주는 메서드 map이 있다.

// 문법
let result = arr.map(function(item, index, array) {
  // 요소 대신 새로운 값을 반환합니다.
});

배열을 정렬하기 위해서는 arr.sort()를 사용한다.

let arr = [ 1, 2, 15 ];

// arr 내부가 재 정렬됩니다.
arr.sort();

alert( arr );  // 1, 15, 2
// 문자열로 인식해 사전편집순으로 정렬되기 때문에 15가 2보다 크다.

// 함수를 사용해 오름차순으로 정렬할 수 있다.
let arr = [ 1, 2, 15 ];

arr.sort(function(a, b) { return a - b; });

alert(arr);  // 1, 2, 15

reverse는 arr의 요소를 역순으로 정렬해주는 메서드이다.

let arr = [1, 2, 3, 4, 5];
arr.reverse();

alert( arr ); // 5,4,3,2,1

str.split(delim) 메서드는 구분자(delimiter) delim을 기준으로 문자열을 쪼개준다.

let names = 'Bilbo, Gandalf, Nazgul';

let arr = names.split(', ');

for (let name of arr) {
  alert( `${name}에게 보내는 메시지` ); // Bilbo에게 보내는 메시지
}

arr.join(glue)은 split과 반대 역할을 하는 메서드이며, 인수 glue를 접착제처럼 사용해 배열 요소를 모두 합친 후 하나의 문자열을 만들어준다.

let arr = ['Bilbo', 'Gandalf', 'Nazgul'];

let str = arr.join(';'); // 배열 요소 모두를 ;를 사용해 하나의 문자열로 합칩니다.

alert( str ); // Bilbo;Gandalf;Nazgul

reduce 메서드를 통해서도 반복작업을 할 수 있다. reduce 메서드는 연산을 다 수행하고 마지막 연산값을 반환한다.

// 문법
let value = arr.reduce(function(accumulator, item, index, array) {
  // ...
}, [initial]);

// 예시
let arr = [1, 2, 3, 4, 5];

let result = arr.reduce((sum, current) => sum + current, 0);

alert(result); // 15

함수의 인수는 다음과 같다.

  • accumulator: 이전 함수 호출의 결과. initial은 함수 최초 호출 시 사용되는 초깃값을 나타냄(옵션)
  • item: 현재 배열 요소
  • index: 요소의 위치
  • array: 배열

계산에 흐름은 다음과 같다.

sumcurrentresult
첫번째 호출011
두번째 호출123
세번째 호출336
네번째 호출6410
다섯번째 호출10515

자바스크립트에서 배열은 독립된 자료형으로 취급되지 않고 객체형에 속하기 때문에, Array.isArray(value) 메서드를 사용할 수 있다. value가 배열이라면 true를, 배열이 아니라면 false를 반환한다.

alert(Array.isArray({})); // false

alert(Array.isArray([])); // true

함수를 호출하는 대부분의 배열 메서드(find, filter, map 등. sort는 제외)는 thisArg라는 매개변수를 옵션으로 받을 수 있다.

arr.find(func, thisArg);
arr.filter(func, thisArg);
arr.map(func, thisArg);
// ...
// thisArg는 선택적으로 사용할 수 있는 마지막 인수입니다.

thisArg는 func의 this가 됩니다.
아래 예시에서 객체 army의 메서드를 filter의 인자로 넘겨주고 있는데, 이때 thisArg는 canJoin의 컨텍스트 정보를 넘겨줍니다.

let army = {
  minAge: 18,
  maxAge: 27,
  canJoin(user) {
    return user.age >= this.minAge && user.age < this.maxAge;
  }
};

let users = [
  {age: 16},
  {age: 20},
  {age: 23},
  {age: 30}
];

// army.canJoin 호출 시 참을 반환해주는 user를 찾음
let soldiers = users.filter(army.canJoin, army);

alert(soldiers.length); // 2
alert(soldiers[0].age); // 20
alert(soldiers[1].age); // 23

반복문

자주쓰는 반복문으로는 while문과 for문이 있다.

// while문 문법
while (condition) {
  // 코드
  // '반복문 본문(body)'이라 불림
}

// while문 예시
let i = 0;
while (i < 3) { // 0, 1, 2가 출력됩니다.
  alert( i );
  i++;
}
// for문 문법
for (begin; condition; step) {
  // ... 반복문 본문 ...
}

// for문 예시
for (let i = 0; i < 3; i++) { // 0, 1, 2가 출력됩니다.
  alert(i);
}
구성요소표현식설명
begini = 0반복문에 진입할 때 단 한 번 실행됩니다.
conditioni < 3반복마다 해당 조건이 확인됩니다. false이면 반복문을 멈춥니다.
bodyalert(i)condition이 truthy일 동안 계속해서 실행됩니다.
stepi++각 반복의 body가 실행된 이후에 실행됩니다.

break문을 통해 무한루프로 도는 반복문을 바로 빠져나올 수 있다.

let sum = 0;

while (true) {

  let value = +prompt("숫자를 입력하세요.", '');

  if (!value) break; // (*)

  sum += value;

}
alert( '합계: ' + sum );

continue 지시자는 전체 반복문을 멈추지 않는다. 대신에 현재 실행 중인 이터레이션을 멈추고 반복문이 다음 이터레이션을 강제로 실행시키도록 합니다(조건을 통과할 때).

for (let i = 0; i < 10; i++) {

  // 조건이 참이라면 남아있는 본문은 실행되지 않습니다.
  if (i % 2 == 0) continue;

  alert(i); // 1, 3, 5, 7, 9가 차례대로 출력됨
}

반복문 안에서 break <labelName>문을 사용하면 레이블에 해당하는 반복문을 빠져나올 수 있다.

outer: for (let i = 0; i < 3; i++) {

  for (let j = 0; j < 3; j++) {

    let input = prompt(`(${i},${j})의 값`, '');

    // 사용자가 아무것도 입력하지 않거나 Cancel 버튼을 누르면 두 반복문 모두를 빠져나옵니다.
    if (!input) break outer; // (*)

    // 입력받은 값을 가지고 무언가를 함
  }
}
alert('완료!');

8일차 공부를 마치며...

파트 3에서는 배열에 메서드를 배우기 때문에 양이 꽤 많았다. 메서드를 어떻게 다루는지 익히기 위해서는 메서드를 활용한 문제를 많이 풀어보면서 복습해 보기로 했다.

profile
웹 프론트엔드 개발자를 꿈꾸고 있습니다!

0개의 댓글