자바스크립트의 배열은 다른 언어들의 배열과는 조금 다른 특징을 갖고 있다. 일반적인 배열은 동일한 크기의 메모리 공간이 빈틈없이 연속적으로 나열되어 있고 요소들은 밀접해 있으며 데이터 타입은 동일하다. 이것을 밀집 배열이라고 하며 인덱스로 빠르게 접근할 수 있다.
JS의 배열은 데이터 타입이 다를 수도 (메모리 공간의 크기가 동일하지 않을 수 있다) 있고 연속적이지 않을 수도 있다. (이것은 희소 배열이라고 한다) JS의 배열은 일반 배열에 비해 인덱스로 요소에 접근하는 경우 성능이 조금 부족할 수 있지만 특정 요소를 검색, 삽입, 제거시엔 더 좋은 성능을 기대할 수 있다.
배열은 다음과 같이 생성할 수 있다.
const arr = [1,2,3];
const arr2 = new Array(1,2,3); //[1,2,3]
const arr3 = Array.of(1,2,3); //[1,2,3]
const arr4 = Array.from({length:3},(_,i)=>i); // [0,1,2]
const arr5 = Array.from('Hello'); // ['H','e','l','l','o']
const arr6 = Array.from({length:2,0:'a',1:'b'); // ['a','b']
생성자 함수로 생성시 인수가 1개이며 숫자이면 그만큼의 empty를 갖는 희소 배열을 생성한다.
Array.of 메서드는 전달된 인수를 요소로 갖는 배열을 생성한다.
Array.from 메서드는 유사 배열 객체나 이터러블 객체를 전달받아 배열로 반환한다. 두 번째 인수로 전달한 콜백함수로 값을 만들면서 요소를 채울 수 있는데, 콜백함수의 반환값으로 만들어진 배열을 돌려준다. 이해를 돕자면, Array.from(obj, (mapFn, thisArg)) = Array.from(obj).map(mapFn,thisArg) 서로 같은 뜻이다.
배열은 인덱스로 참조할 수 있고, 존재하지 않는 곳에 접근하면 undefined이다. 존재하지 않는 곳에 할당하면 생성되고, 현재 배열보다 큰 곳에 할당하면 그 사이는 희소 배열이 된다. delete로 요소를 삭제할 수 있지만 그 빈 공간은 희소 배열이 되므로 만약 arr[1]부터 1개를 제거하고 싶다면 arr.splice(1,1) 처럼 사용하자. 이 경우 length 값도 2로 바뀐다. length 프로퍼티 값은 항상 희소 배열의 실제 요소 개수보다 큼을 생각하자. delete 경우 3 그대로다.
배열 메서드는 원본 배열을 직접 변경하는 메서드와 아닌 메서드가 있다.
const arr = [1,2,2,3];
Array.isArray([]); // true
arr.indexOf(2,2); // 2, 없으면 -1, 두 번째 인수는 시작 위치이며 생략 가능
// 직접 변경
arr.push(3,4); // [1,2,2,3,3,4], 6 반환
arr.pop(); // [1,2,2,3,3], 4 반환
arr.unshift(4,3); // [4,3,1,2,2,3,3], 7 반환
arr.shift(); // [3,1,2,2,3,3], 4 반환
arr.splice(1,2,10,20); // [3,10,20,2,3,3], [1,2] 반환, (시작점, 제거 개수(생략시 시작점부터 끝), 제거 한 곳에 넣을 것)
arr.reverse(); // [3,3,2,20,10,3], [3,3,2,20,10,3] 반환
arr.fill(0,2,5); // [3,3,0,0,0,3], [3,3,0,0,0,3] 반환 (채울 것, 시작, 끝)
[1,[2,[3,[4]]]].flat(2); // [1,2,3,[4]], 전달한 값만큼 평탄화 함, 기본값 1
// 직접 변경하지 않는 것
const arr2 = [5,6];
const arr3 = [7,8];
let result = arr2.concat(arr3); // [5,6,7,8] 반환
let result2 = arr2.slice(0,2); // [5,6] 반환, 모두 생략시 모든 요소를 얕은 복사, (시작, 끝-1)
let result3 = arr2.join(':'); //'5:6' 반환, 기본값은 ,
let result4 = arr2.includes(2,0); //false (검색, 시작점)
forEach, map, filter, reduce, some, every, find, findIndex 메서드는 역할이 비슷하다.
모두 자신을 호출한 배열의 요소를 순회하며 콜백 함수를 호출하고 원본 배열을 변경하지 않는다.
그렇지만 각각 조금의 차이점이 있다.
forEach는 호출, map은 호출 후 반환값으로 구성된 배열 반환, filter는 반환값이 true인 요소로만 구성된 배열 반환, reduce는 하나의 결과 반환, some은 반환값이 하나라도 참이면 true 반환, every는 반환값이 모두 참이면 true 반환, find는 반환값이 true인 첫 번째 요소 반환, findIndex는 반환값이 true인 첫 번째 요소의 인덱스 반환이다.
대표적으로 forEach와 reduce만 살펴보자. (대표적인 예시와 특이한 예시이므로)
class Numbers {
numberArray = [];
multiply(arr) {
arr.forEach(item => this.numberArray.push(item * item));
}
}
const numbers = new Numbers();
numbers.multiply([1,2,3]);
console.log(numbers.numberArray); // [1,4,9]
const values = [1,2,3,4,5,6];
const average = values.reduce((acc,cur,i,{length})=>{
return i === length - 1 ? (acc + cur) / length : acc + cur;
// 마지막 순회가 아니면 누적값 반환, 마지막이면 평균 반환
}, 0);
console.log(average); // 3.5
위 예제에선 전에 알아본 화살표 함수로 콜백 함수의 this 바인딩 문제 해결도 잘 사용한 모습이다.
forEach는 break, continue 등으로 중단 할 수 없으며 희소 배열 부분은 패스한다.
reduce는 (함수(누적값(마지막 반환값), 현재요소값, 현재인덱스, 호출한 배열(this)),초기값) 형태로 호출한다. 초기값은 생략시 [0]이 누적값, [1]이 현재요소값인 상태로 순회한다.


다음과 같이 초기값 여부에 따라 인덱스가 달라짐에 유의하자.