배열(array)은 여러 개의 값을 순차적으로 나열한 자료구조이다.
자바스크립트에 따로 배열이라는 타입은 존재하지 않는다. 배열은 객체 타입이다.
배열은 객체지만 일반 객체와는 구별된 독특한 특징이 있다.
구분 | 객체 | 배열 |
---|---|---|
구조 | 프로퍼티 키와 프로퍼티 값 | 인덱스와 요소 |
값의 참조 | 프로퍼티 키 | 인덱스 |
값의 순서 | X | O |
length 프로퍼티 | X | O |
배열이 가지고 있는 값을 요소(element)라고 부르며, 각 요소는 배열에서 자신의 위치를 나타내는 인덱스(index)를 갖는다.
인덱스는 배열의 요소에 접근할 때 사용하며, 대부분 0부터 시작한다.
요소 인덱스로 문자열을 사용할 수 없으며 무조건 정수만 허용한다.
자바스크립트 배열은 길이(요소의 개수)도, 각 요소의 자료형도 고정되어 있지 않다.
배열은 배열의 길이를 나타내는 length 프로퍼티를 갖는다.
배열은 인덱스로 표현되는 값의 순서와 length 프로퍼티를 갖기 때문에 for 문을 통해 순차적으로 요소에 접근할 수 있다.
ECMAScript® 2023 Language Specification
JavaScript 배열은 ‘밀집 배열’이 아니라 ‘희소 배열’이다.
밀집 배열(dense array)
일반적으로 자료구조에서 말하는 배열로, 동일한 크기의 메모리 공간이 빈틈없이 연속적으로 나열된 자료구조이다.
즉, 배열의 요소는 하나의 데이터 타입으로 통일되어 있으며 서로 연속적으로 인접해 있다.
따라서 아래와 같이 인덱스를 통해 단 한 번의 연산으로 임의의 요소에 접근할 수 있어 효율적이다.
(검색 대상 요소의 메모리 주소) = (배열의 시작 메모리 주소) + (인덱스) * (요소의 바이트 수)
하지만 정렬되지 않은 배열에서 특정 요소를 검색하는 경우 배열의 모든 요소를 처음부터 특정 요소를 발견할 때까지 선형 검색(linear search, 시간 복잡도 O(n))해야 한다는 점과 배열에 요소를 삽입/삭제하는 경우 배열의 요소를 연속적으로 유지하기 위해 요소를 이동시켜야 하는 단점도 있다.
자바스크립트에서는 JavaScript 형식화 배열(typed array)을 통해 위 메커니즘을 제공한다.
형식화 배열은 Array.isArray() 호출에서 false를 반환하기 때문에 JavaScript 배열과 혼동되지 않고, JavaScript 배열에 이용할 수 있는 모든 메서드가 지원되지는 않는다(특히 push나 pop).
희소 배열(sparse array)
JavaScript 배열은 자료구조에서 말하는 일반적인 의미의 배열과 다르며, 일반적인 배열의 동작을 흉내 낸 특수한 객체라고 할 수 있다.
배열의 요소를 위한 각각의 메모리 공간은 동일한 크기를 갖지 않아도 되며, 연속적으로 이어져 있지 않을 수도 있다.
자바스크립트 배열은 인덱스를 나타내는 문자열을 프로퍼티 키로 가지며, length 프로퍼티를 갖는 특수한 객체이다. 프로퍼티 값으로는 배열의 요소를 갖는다.
JavaScript 배열은 해시 테이블로 구현된 객체이므로 인덱스로 요소에 접근하는 경우 일반적인 배열보다 성능적인 면에서 느릴 수 밖에 없는 구조적인 단점이 있다.
하지만 요소를 삽입 또는 삭제하는 경우에는 일반적인 배열보다 빠른 성능을 기대할 수 있다.
인덱스로 배열 요소에 접근할 때 일반적인 배열보다 느릴 수 밖에 없는 구조적인 단점을 보완하기 위해 대부분의 모던 자바스크립트 엔진은 배열을 일반 객체와 구별하여 좀 더 배열처럼 동작하도록 최적화하여 구현 했다.
length 프로퍼티는 요소의 개수, 즉 배열의 길이를 나타내는 0 이상의 정수를 값으로 갖는다.
length 프로퍼티의 값은 빈 배열일 경우 0이며, 빈 배열이 아닐 경우 가장 큰 인덱스에 1을 더한 것과 같다.
length 프로퍼티의 값은 배열에 요소를 추가하거나 삭제하면 자동 갱신된다.
length 프로퍼티 값은 요소의 개수, 즉 배열의 길이를 바탕으로 결정되지만 임의의 숫자 값을 명시적으로 할당할 수도 있다.
현재 length 프로퍼티 값보다 작은 숫자 값을 할당하면 배열의 길이가 줄어든다. 만약 현재 length 프로퍼티 값보다 큰 숫자 값을 할당하면 length 프로퍼티 값은 변경되지만 실제로 배열의 길이가 늘어나지는 않는다. 값 없이 비어 있는 요소를 위해 메모리 공간을 확보하거나 빈 요소를 생성하지 않는다.
일반적인 배열과는 달리, 희소 배열은 length와 배열 요소의 개수가 일치하지 않는다. 희소 배열의 length는 희소 배열의 실제 요소 개수보다 언제나 크다.
<aside> <img src="/icons/chat_gray.svg" alt="/icons/chat_gray.svg" width="40px" /> 자바스크립트는 문법적으로 희소 배열을 허용하지만, 희소 배열은 연속적인 값의 집합이라는 배열의 기본적인 개념과 맞지 않으며 성능에도 좋지 않은 영향을 준다. 최적화된 모던 자바스크립트 엔진은 요소의 타입이 일치하는 배열을 생성할 때 일반적인 의미의 배열처럼 연속된 메모리 공간을 확보하기 때문에 배열은 같은 타입의 요소만을 가지도록 권장한다.
</aside>
배열 리터럴은 가장 일반적이고 간편한 배열 생성 방식이다.
배열 리터럴은 0개 이상의 요소를 쉼표로 구분하여 대괄호로 묶는다.
배열 리터럴은 객체 리터럴과 달리 프로퍼티 키가 없고 값만 존재한다.
const arr1 = [];
console.log(arr.length); // -> 0
const arr2 = [1, 2, 3];
const arr3 = [1, , 3]; // 희소 배열 생성
console.log(arr); // [1, empty, 3];
console.log(arr[1]); // -> undefined(프로퍼티 키가 '1'인 프로퍼티가 존재하지 않기 때문)