자바스크립트 배열은 배열이 아니다.

·2024년 1월 6일
15

프론트엔드

목록 보기
9/11

일반적인 배열

일반적으로 배열이라는 자료구조의 개념은 동일한 크기의 메모리 공간이 빈틈없이 연속적으로 나열된 자료 구조를 말합니다. 배열의 요소는 하나의 타입으로 통일되어 있으며 서로 연속적으로 인접해 있습니다. 이러한 배열을 밀집배열이라고 합니다.

또한 인덱스를 통해 단 한번의 연산으로 임의의 요소에 접근 (임의 접근(random access), 시간 복잡도 O(1)) 할 수 있습니다. 이는 매우 효율적이며 고속으로 동작합니다.

검색 대상 요소의 메모리 주소 = 배열의 시작 메모리 주소 + 인덱스 * 요소의 바이트 수

예를 들어, 위 그림처럼 메모리 주소 1000에서 시작하고 각 요소의 크기가 8byte인 배열을 생각해보면

인덱스가 0인 요소의 메모리 주소 : 1000 + 0 x 8 = 1000
인덱스가 1인 요소에 메모리 주소 : 1000 + 1 x 8 = 1008
인덱스가 2인 요소에 메모리 주소 : 1000 + 2 x 8 = 1016

이처럼 배열은 인덱스를 통해 효율적으로 요소에 접근할 수 있다는 장점이 있습니다.

중요한 키워드는 '빈틈없이''연속적' 입니다. 이러한 특징 덕분에 배열은 인덱스를 통해 효율적으로 요소에 접근 할 수 있다는 장점이 있습니다. 생각해보면 아주 당연한 말인 것 같습니다. 어떤 타입의 요소가 할당될 것인지, 얼마큼의 메모리를 할당할 것인지 이미 다 아는 상태기 때문에 탐색이 빠를 수밖에 없겠죠.

하지만 배열의 요소를 삽입하거나 삭제하는 경우, 배열 요소를 연속적으로 유지하기 위해 요소를 이동시켜야 하는 단점이 있습니다.

요소 삽입

삽입할 때 연속적으로 이어져있던 요소들이 뒤로 밀립니다.

요소 삭제


빈틈없이 연속적인 특징을 유지하기 위해 삭제된 공간을 채워야 하기 때문에, 요소들이 한칸씩 앞으로 이동하는 것을 볼 수 있습니다.

자바스크립트 배열

자바스크립트 배열은 위에 설명한 것 처럼 우리가 알고 있는 일반적인 배열의 특성과 다릅니다. 자바스크립트의 배열은 Array.prototype 객체를 상속받는 인스턴스입니다. 이 Array.prototype 은 Object.prototype을 상속받고 있습니다.

그러므로 자료구조에서 말하는 배열과는 다르며, 객체로 만들어져서 마치 배열과 같이 작동하기 때문에 배열이라고 부릅니다. 결론적으로 자바스크립트의 배열은 일반적인 배열의 동작을 흉내낸 객체입니다.

이러한 특성때문에 자바스크립트의 배열은 다양한 타입의 요소들을 가질 수 있는 것입니다.

const mixedArray = [10, "Hello", true, { key: "value" }];

자바로 자바스크립트의 mixedArray 를 흉내내려면 배열을 사용할 수 없고 Object를 사용해야 합니다.

Object[] mixedArray = new Object[4]; // Object 배열 선언

mixedArray[0] = 10; 
mixedArray[1] = "Hello"; 
mixedArray[2] = true; 
mixedArray[3] = new SomeClass(); 

자바스크립트 배열의 특성

  • 배열의 요소를 위한 각각의 메모리 공간은 동일한 크기를 갖지 않아도 됩니다.
  • 연속적으로 이어져 있지 않을 수도 있습니다. (이러한 배열을 희소 배열이라 합니다.)

정 요소를 탐색하거나 요소를 삽입 또는 삭제하는 경우에는 일반적인 배열보다 빠른 성능을 기대할 수 있습니다.

하지만 자바스크립트 배열은 해시 테이블로 구현된 객체이므로 인덱스로 배열 요소에 접근하는 경우, 일반적인 배열보다 성능적인 면에서 느릴 수 밖에 없는 구조적인 단점을 갖습니다. (일반적인 배열이 빠른 이유)

인덱스로 배열 요소에 접근할 때 일반적인 배열보다 느릴 수 밖에 없는 구조적인 단점을 보완하기 위해 대부분의 모던 자바스크립트 엔진은 배열을 일반 객체와 구별하여 보다 배열처럼 동작하도록 최적화하여 구현했습니다.

여기서 헷갈리시면 안되는 것, JS 엔진이 최적화 한것은 JS의 배열입니다.
객체로 이루어진 JS 의 배열을 최적화 해서 JS의 객체와 비교해서 더 빠른 것 입니다.

JS 엔진의 배열 최적화

const arr = [];
console.time('Array Performance Test');
for (let i = 0; i < 10000000; i++) {
  arr[i] = i;
}
console.timeEnd('Array Performance Test');
// 약 340ms

const obj = {};
console.time('Object Performance Test');
for (let i = 0; i < 10000000; i++) {
  obj[i] = i;
}
console.timeEnd('Object Performance Test');
// 약 600ms

다시 한 번 강조합니다.
JS의 배열은 객체이지만, JS 엔진이 JS의 일반 객체와 배열을 구별해 배열을 최적화 했다는 것입니다. 그렇기 때문에 arr 의 속도는 약 340ms로 js의 객체보다 속도가 빠른 것입니다.

번외 : 자바스크립트는 희소배열인가요?

자바스크립트는 밀집 배열이자 희소 배열입니다. 처음에 자바스크립트 배열을 초기화했을때는 데이터를 연속적으로 붙여놓습니다. 그리고 새로운 데이터가 추가되고 나서는 희소배열의 형태로 바뀝니다.

const arr = [1,2,3];
arr[50] = 50;

24번째 인덱스를 체크해보면 undefine 가 찍히는 것을 볼 수 있습니다. 그리고 비록 인덱스 3~49는 비어 있지만, 배열의 길이는 51이 됩니다.

돌아보기

  • 자바스크립트는 해시 테이블로 구현된 객체이다.

  • [ 인덱스로 접근 ] 자바스크립트는 객체라는 특성 때문에 인덱스로 배열 요소에 접근하는 경우, 일반적인 배열보다 성능적인 면에서 느릴 수 밖에 없는 구조
    => 일반적인 배열과 자바스크립트 배열의 특성을 제대로 이해하고 이유를 설명해보세요. 배열의 자료구조의 특성을 이해하면 답이 쉽게 나옵니다.

  • [ 특정 요소를 탐색, 요소 삽입, 삭제 ] 이 경우 일반적인 배열보다 빠른 성능을 기대할 수 있음
    => 자바스크립트 배열은 해시 테이블로 구현된 객체이기 때문입니다.

  • 하지만 JS 엔진은 JS의 일반 객체와 배열을 구별해 배열처럼 동작해 최적화함
    => JS의 object 와 array 를 비교해 JS array 를 최적화 한 것을 이해해야 합니다.

레퍼런스

https://developer.mozilla.org/ko/docs/Learn/JavaScript/First_steps/Arrays#%EB%B0%B0%EC%97%B4%EC%9D%B4%EB%9E%80

https://www.youtube.com/watch?v=MIrMIpqQ7kc&t=164s
https://blog.devgenius.io/arrays-and-array-in-javascript-345b4f87a232

profile
My Island

6개의 댓글

comment-user-thumbnail
2024년 1월 6일

중요 키워드에 이해쉬운 그림과 번외까지 완벽하네요! 블로그 자주 봐야겠네요!

1개의 답글
comment-user-thumbnail
2024년 1월 14일

중요한 내용이네요 ㅎㅎ 잘 보고 갑니다!

1개의 답글
comment-user-thumbnail
2024년 1월 16일

루프 밖에서 인덱스를 지정하여 get set 하는 건 애초에 하면 안되는 짓인데 거기에 큰 의미를 둘 필요는 없는 것 같습니다. 오히려 엔진 구현체가 어떤 상황에 최적화 하는지 혹은 할 것 같은지 생각해보는 게 유의미할 것 같아요. 암튼 결국에 method를 통한 대화적 접근이 옳은 길이라 생각함. js에 주화입마하면 스스로 바벨화 되서 무너짐. 조심.

1개의 답글