Javascript
- 처음부터 다시 보는 딥 다이브
자바스크립트의 배열
자바스크립트에서 배열이란 타입은 존재하지 않으며, 배열 또한 객체 타입이다.
- 배열은 객체이지만 일반 객체와는 구분되는 차이점이 존재한다.
- 객체는 프로퍼티와 키 값을 가지지만, 배열은 인덱스와 요소를 가진다.
- 객체는 프로퍼티 키를 참조하여 값을 나타내지만, 배열은 인덱스를 참조하여 값을 나타낸다.
- 객체는 값의 순서를 갖지 않지만, 배열은 값의 순서를 갖는다.
- 객체는 length 프로퍼티를 갖지 않지만, 배열은 갖는다.
자바스크립트 배열의 특징
- 배열은 두 가지로 구분된다.
밀집 배열과 희소 배열로 구분된다.
밀집 배열
자료구조에서 일반적으로 언급하는 동일한 크기의 메모리 공간이 빈틈없이 연속적으로 나열된 자료 구조를 말한다.
- 배열의 요소들은 하나의 데이터 타입으로 통일, 서로 연속적으로 인접해 있는 형태를 의미한다.
- 연속적인 구조이기 때문에, 인덱스를 통해 단 한 번의 연산으로 요소에 접근할 수 있다.
정렬되지 않은 배열의 특정 요소를 검색하는 경우, 배열의 모든 요소를 처음부터 원하는 요소를 찾을 때까지 순회해야 하므로 시간 복잡도가 증가한다.
배열의 요소를 삭제하거나 삽입하는 경우에도 연속적인 구조를 유지하기 위해 요소를 작업 시마다 이동시켜야 하는 단점이 존재한다.
희소 배열
- 배열의 요소들이 각각의 메모리 공간의 크기가 동일하지 않아도 되고, 연속적으로 이루어져 있지 않아도 된다.
이를 희소 배열이라고 하며 자바스크립트의 배열은 희소 배열이다.
- 자바스크립트의 배열은 일반적인 배열의 동작을 흉내낸 특수한 객체이다.
자바스크립트 배열은 인덱스를 나타내는 문자열을 프로퍼티 키로 가지며, length 프로퍼티를 갖는 특수한 객체이다.
배열의 요소는 사실상 프로퍼티 값이며, 자바스크립트에서 사용할 수 있는 모든 값은 객체의 프로퍼티 값이 될 수 있으므로 어떤 타입의 값도 배열의 요소가 될 수 있다.
length 프로퍼티와 희소 배열
- 기본적으로는 배열의 길이를 바탕으로 결정되지만, 임의의 숫자값을 명시적으로 할당할 수도 있다.
- 이 경우, 현재 프로퍼티의 length 프로퍼티보다 작은 숫자 값을 할당할 경우 배열의 길이가 줄어든다.
- 현재 프로퍼티의 length 프로퍼티 값보다 큰 숫자 값을 할당할 경우는 length 프로퍼티 값은 변경되지만 실제 배열의 길이는 변함이 없다.
- length 프로퍼티가 값 없이 비어있는 요소를 위해 메모리 공간을 확보하지 않으며, 빈 요소를 생성하지 않음을 의미한다.
- 배열의 요소가 연속적으로 위치하지 않고 일부가 비어 있는 배열인 희소 배열이다.
- 자바스크립트 엔진은 희소 배열을 문법적으로 허용한다.
- 희소 배열을 의도적으로 생성하는 일은 많지 않으며 가능한 사용하지 않는 것이 좋다.
- 배열에는 같은 타입의 요소를 의도적으로 연속시켜 배치하는 것이 좋다.
배열 생성
new Array
- 전달된 인수가 1개이고 숫자일 경우, length 프로퍼티 값이 인수인 배열을 생성한다.
- 전달된 인수가 없는 경우, 빈 배열을 생성한다.
- 전달된 인수가 2개 이상이거나 숫자가 아닌 경우, 인수를 요소로 갖는 배열을 생성한다.
1
2
3
4
5
new Array(); // []
new Array(3); // [<3 empty items>]
new Array(-1); // RangeError
new Array(1, 2, 3); // [1,2,3]
new Array({}); // [{}]
Array.of
- ES6에 도입되었다.
- 전달된 인수를 갖는 배열을 생성한다.
- 인수가 1개이고 숫자여도 인수를 요소로 갖는 배열을 생성한다.
1
2
3
Array.of(1); // [1]
Array.of(1, 2, 3); // [1,2,3]
Array.of("arr"); // ["arr"]
Array.from
- ES6에 도입되었다.
- 유사 배열 객체 또는 이터러블 객체를 인수로 전달받아 배열로 변환 후 반환한다.
- 두 번째 인수로 함께 전달할 콜백 함수를 통해 값을 만들면서 요소를 채울 수도 있다.
1
2
3
Array.from({ legnth: 2, 0: "1", 1: "2" }); // [1,2]
// 문자열은 이터러블 객체이기도 하다.
Array.from("Hi"); // ["H","i"]
배열 참조
- 배열에 대괄호를 사용하며 인덱스를 삽입한다.
- 정수로 평가되는 표현식이면 인덱스로 사용이 가능하다.
- 존재하지 않는 요소에 접근하면 undefined를 반환한다.
1
arr[1];
배열 요소의 추가,갱신,삭제
- 배열도 객체이므로 객체의 프로퍼티 동작으로 추가할 수 있는 것처럼 배열에도 요소를 동적으로 추가할 수 있다.
- 현재 배열의 length 프로퍼티 값보다 큰 인덱스로 새로운 요소를 추가하려고 하면 희소 배열이 된다.
- 값이 할당되지 않은 희소 배열의 요소들은 생성되지 않는다.
- length 프로퍼티 값은 변하나 실질적인 배열 안의 요소 개수에는 변함이 없다.
- 0 이상의 정수로 인덱싱에 사용해야 한다.
- 정수 이외의 값을 인덱싱에 사용하면 요소가 생성되는 것이 아니라 프로퍼티가 생성된다.
- 프로퍼티는 length 프로퍼티 값에 영향을 주지 않는다.
- 배열도 객체이므로 배열의 특정 요소를 삭제하기 위해 delete 연산자를 사용할 수 있다.
- delete 연산자를 통해 배열의 요소를 삭제하는 것은 객체의 프로퍼티를 삭제하는 것과 동일하다.
- 요소의 값이 삭제되면 희소 배열이 된다.
- 이 때, length 프로퍼티에는 영향을 주지 않는다.
- 희소 배열을 만들지 않으면서 요소를 삭제하고 싶은 경우 Array.prototype.splice를 사용한다.
배열 메서드
mutator, accessor
메서드로 나뉜다.- mutator : 호출한 원본 배열을 직접 변경한다.
- accessor : 원본 배열을 직접 변경하지 않고 새로운 배열을 반환한다.
- 자바스크립트 배열은 다양한 빌트인 함수를 제공한다.
- 배열 생성자 함수는 다양한 정적 메서드를 제공한다.
- 배열 객체의 Array.prototype은 프로토타입을 제공한다.
- 결과물을 반환하는 패턴은 두가지이다.
- 원본 배열을 직접 변경하는 메서드는 외부 상태를 변경하는 사이드 이펙트를 발생시킬 수 있기 때문에 주의해야 한다.
- 원본 배열을 직접 변경하지 않고 새로운 배열을 생성하여 반환하는 메서드를 사용하는 편이 좋다.
Array.isArray
- 생성자 함수의 정적 메서드, 전달되는 인수가 배열인지를 판단한다.
Array.prototype.indexOf
- 원본 배열에서 인수로 전달한 요소를 검색하여 인덱스를 반환한다.
- 검색하는 요소의 첫 번째 요소를 반환한다.
- 요소가 존재하지 않으면
-1
을 반환하여 요소가 존재하는지 확인하는 데 유용하다.
1
2
3
4
const arr = [1, 2, 3];
arr.indexOf(-1); // 존재하지 않는 요소를 검색한다.
arr.indexOf(2, 2); // 두 번째 인덱스 2를 검색한다.
Array.prototype.push
mutator method
- 인수로 전달받은 모든 값을 배열의 마지막 요소로 추가한다.
- 변경된 length 프로퍼티를 반환한다.
- 스프레드 문법을 사용하는 것이 더 좋다.
- 객체에 스프레드 문법을 사용하여도 프로토타입 체인의 속성을 복사하진 않는다.
Array.prototype.pop
mutator method
- 원본에서 마지막 요소를 제거하고 제거한 요소를 반환한다.
- 원본 배열이 빈 배열이면 undefined를 반환한다.
Array.prototype.unshift
mutator method
인수로 전달받은 모든 값을 배열의 선두에 추가하고, 변경된 length 프로퍼티 값을 반환한다.
Array.prototype.shift
mutator method
- 원본 배열에서 첫 번째 요소를 제거하고 제거한 요소를 반환한다.
- 원본 배열이 빈 배열이면 undefined를 반환한다.
Array.prototype.concat
- 인수로 전달된 값들을 원본 배열의 마지막에 요소로 추가한 새로운 배열을 반환한다.
- 인수로 전달한 값이 배열일 경우, 배열을 해체하여 새로운 배열을 반환한다.
- push, unshift 메서드는 concat으로 대체 가능하다.
- 원본 배열을 직접 변경하지 않고, 새로운 배열을 반환한다.
- 스프레드 문법으로 대체 가능하다.
Array.prototype.splice
- 원본 배열의 중간에 요소를 추가하거나 중간에 있는 요소를 제거할 때 사용한다.
3개의 매개변수를 갖는데, 첫 번째에는 삭제 시작 인덱스, 두 번째에는 시작 인덱스로부터 삭제할 요소의 개수, 세 번째에는 요소를 삭제 후 인덱스로부터 추가할 데이터를 받는다.
1
2
const arr = [1, 2, 3, 4];
const newArr = arr.splice(1);
Array.prototype.slice
accessor method
- 인수로 전달된 범위의 요소들을 복사하여 배열로 반환한다.
- 두 개의 매개변수를 가지며, 첫 번째 인수로는 복사를 시작할 인덱스, 두 번째 인수로는 복사 끝 인덱스를 나타낸다.
- 얕은 복사를 통해 새로운 배열을 생성한다.
Array.prototype.join
- 원본 배열의 모든 요소를 문자열로 반환한 후, 인수로 전달받은 문자열, 즉 구분자로 연결하여 문자열을 반환한다.
1
2
3
4
const arr = [1, 3, 3, 5];
arr.join(); // 1,3,3,5
aarr.join(""); // 1335
Array.prototype.reverse
mutator method
원본 배열의 순서를 반대로 뒤집는다.
Array.prototype.fill
mutator method
- ES6에 도입
- 인수로 전달받은 값으로 배열의 처음부터 끝까지 채운다.
- 3개의 매개변수를 받는데, 첫 번째는 초기화 시킬 값, 두 번째는 시작 인덱스, 세 번째는 끝 인덱스 값을 입력한다.
1
2
3
const arr = new Array(3); // [<3 empty items>]
arr.fill(1); // [1,1,1]
arr.fill(100, 1, 2);
Array.prototype.includes
- ES7에 도입
- 배열 내에 특정 요소가 포함되어 있는지 확인하여 boolean 값을 반환한다.
- 2개의 매개 변수를 가지며, 첫 번째는 검색할 값, 두 번째는 시작 인덱스를 입력한다.
- indexOf와의 차이점은 indexOf는 없으면 -1임을 확인해야 하며, 배열에 NaN이 있다면 판별할 수 없다.
1
2
[NaN].indexOf(NaN); // -1
[NaN].includes(NaN); // true
Array.prototype.flat
- ES10에 도입
- 인수로 전달한 깊이만큼 재귀적으로 평탄화한다.
- 기본값은 1이기 때문에 아무것도 전달하지 않으면 한 번 평탄화를 진행한다.
- 인수로
Infinity
를 전달하면 전부 평탄화한다.
1
2
3
const arr = [1, 1, 1, [2, 2, 2, [3, 3, 3]]];
const newArr1 = arr.flat(); // [1,1,1,2,2,2,[3,3,3]]
const newArr2 = arr.flat(2); // [1,1,1,2,2,2,3,3,3]