학습 자료

배열 — 값을 순서대로 담는다


Article

지금까지 변수 하나에 값 하나를 담았다. 학생 서른 명의 점수를 다루려면 변수를 서른 개 만들어야 할까.

여러 값을 순서대로 하나에 담는 그릇이 배열이다.

만들고 꺼낸다

대괄호 [ ] 안에 값을 쉼표로 나열한다.

const fruits = ["사과", "바나나", "포도"];

console.log(fruits[0]);
console.log(fruits[2]);
console.log(fruits.length);
console.log(fruits[3]);
콘솔
  • '사과'
  • '포도'
  • 3
  • undefined

5강의 문자열과 똑같다. 인덱스는 0부터 시작하고, 마지막 인덱스는 length - 1이다. 없는 자리를 꺼내면 에러가 아니라 undefined가 나온다.

인덱스와 값
  1. "사과"인덱스 0
  2. "바나나"인덱스 1
  3. "포도"인덱스 2

배열은 타입을 가리지 않는다. 섞어 담아도 된다.

const mixed = [1, "글자", true, null, [1, 2]];

console.log(mixed[4][1]);
console.log(typeof mixed);
console.log(Array.isArray(mixed));
콘솔
  • 2
  • 'object'
  • true

4강에서 본 대로 typeof'object'를 준다. 배열은 객체의 한 종류이기 때문이다. 배열인지 확인하려면 Array.isArray()를 쓴다.

문자열과 다른 점 — 배열은 바뀐다

5강에서 문자열은 고칠 수 없다고 했다. 배열은 다르다.

const word = "hello";
word[0] = "H";
console.log(word);

const arr = ["h", "e"];
arr[0] = "H";
console.log(arr);
콘솔
  • hello
  • ['H', 'e']

배열은 자리에 값을 직접 넣을 수 있다. const로 선언했는데도 된다. 3강에서 예고한 그 이야기다.

const 가 지키는 것

이름과 배열의 연결만 잠겼다. 배열 내용은 열려 있다

arr
0x1a2b
['H', 'e']

arr다른 배열을 통째로 넣는 것은 막히지만, 배열 안을 고치는 것은 막지 않는다.

값을 넣고 뺀다

끝과 앞에서 각각 넣고 뺀다. 네 개가 짝을 이룬다.

메서드위치하는 일반환값
push넣는다새 length
pop뺀다빼낸 값
unshift넣는다새 length
shift뺀다빼낸 값
const stack = ["a", "b"];

stack.push("c");
console.log(stack);

const last = stack.pop();
console.log(last, stack);

stack.unshift("z");
console.log(stack);
콘솔
  • ['a', 'b', 'c']
  • 'c' ['a', 'b']
  • ['z', 'a', 'b']

pushpop은 배열의 끝만 건드려서 빠르다. 13강에서 본 콜 스택이 정확히 이렇게 동작한다. 쌓고, 위에서부터 꺼낸다.

unshiftshift는 앞에 넣고 빼므로 나머지 전부의 인덱스가 밀린다. 배열이 길면 그만큼 느려진다.

10만 개짜리 배열에 하나 넣기 (상대적 비용)
push (끝)1
unshift (앞)40

slice 와 splice — 헷갈리는 한 쌍

이름이 비슷한데 동작이 완전히 다르다. 하나는 원본을 지키고, 하나는 원본을 부순다.

slice 와 splice

slice

잘라서 복사한다

  • 원본을 바꾸지 않는다
  • 잘라낸 새 배열을 돌려준다
  • slice(시작, 끝) — 끝은 미포함
  • 인자 없이 부르면 전체 복사

splice

잘라내고 끼워 넣는다

  • 원본을 직접 바꾼다
  • 제거된 값들을 돌려준다
  • splice(시작, 개수, 넣을값…)
  • 삭제·삽입·교체를 모두 한다
const original = ["a", "b", "c", "d"];

const copied = original.slice(1, 3);
console.log(copied);
console.log(original);

const removed = original.splice(1, 2);
console.log(removed);
console.log(original);
콘솔
  • ['b', 'c']
  • ['a', 'b', 'c', 'd']
  • ['b', 'c']
  • ['a', 'd']

돌려주는 값은 같지만, slice 뒤의 original은 멀쩡하고 splice 뒤의 original은 잘려나갔다.

splice는 넣기도 한다. 세 번째 인자부터가 끼워 넣을 값이다.

const items = ["a", "d"];

items.splice(1, 0, "b", "c");
console.log(items);
콘솔
  • ['a', 'b', 'c', 'd']

인덱스 1에서 0개를 지우고 "b", "c"를 넣었다.

원본을 바꾸는가, 아닌가

배열 메서드를 볼 때 가장 먼저 물어야 할 질문이다. 이 구분을 놓치면 4강에서 본 참조 타입 버그를 만난다.

원본을 바꾼다원본을 지킨다
push · popslice
unshift · shiftconcat
splicejoin
sort · reverseindexOf · includes

sort가 원본을 바꾼다는 걸 모르고 쓰다 사고가 난다.

const scores = [3, 1, 2];
const sorted = scores.sort();

console.log(sorted);
console.log(scores);
콘솔
  • [1, 2, 3]
  • [1, 2, 3]

scores까지 정렬돼버렸다. 원본을 지키려면 복사한 뒤 정렬한다.

const scores = [3, 1, 2];
const sorted = scores.slice().sort();

console.log(sorted, scores);
콘솔
  • [1, 2, 3] [3, 1, 2]

찾는다

const fruits = ["사과", "바나나", "포도"];

console.log(fruits.indexOf("바나나"));
console.log(fruits.indexOf("수박"));
console.log(fruits.includes("포도"));
콘솔
  • 1
  • -1
  • true

indexOf는 못 찾으면 -1을 준다. 0이 falsy라는 걸 기억하면 왜 if (fruits.indexOf(x))가 위험한지 보인다. 첫 번째 요소를 찾아도 0이라 거짓 취급된다.

있는지만 알고 싶다면 includes를 쓴다. 의도가 분명하고 함정이 없다.

복사할 때 조심한다

4강의 그 이야기다.

const a = [1, 2, 3];
const b = a;
b.push(4);

console.log(a);
콘솔
  • [1, 2, 3, 4]

=로는 복사되지 않는다. 주소만 복사되어 둘이 같은 배열을 본다.

진짜 복사본을 만드는 방법은 여럿이다.

const a = [1, 2, 3];

const copy1 = a.slice();
const copy2 = [...a];
const copy3 = Array.from(a);

copy2.push(99);
console.log(a, copy2);
콘솔
  • [1, 2, 3] [1, 2, 3, 99]

[...a]스프레드 문법이다. 18강에서 제대로 다룬다. 지금은 배열을 펼쳐 새 배열에 담는다고 이해하면 된다.

정리하면

  • 배열은 값을 순서대로 담고, 인덱스는 0부터 시작한다.
  • typeof'object'를 준다. 배열 확인은 Array.isArray()로 한다.
  • const 배열도 내용은 바뀐다. const이름과 배열의 연결만 지킨다.
  • push/pop은 끝을, unshift/shift는 앞을 다룬다. 앞쪽 조작이 더 비싸다.
  • slice는 원본을 지키고 splice는 원본을 부순다. sortreverse도 원본을 바꾼다.
  • indexOf는 못 찾으면 -1, 첫 요소면 0이다. 존재 확인은 includes를 쓴다.
  • =로는 복사되지 않는다. slice()[...arr]를 쓰되, 얕은 복사임을 기억한다.

다음 강의에서는 배열의 진짜 무기를 꺼낸다. map, filter, reduce다. 11강에서 배운 콜백이 여기서 빛을 발한다.

배열 — 값을 순서대로 담는다 — 디코드랩(DCODELAB)