
이번편에는 JS의 배열에 대해 deep하게 들어가볼까 한다.
- 배열이란?
- 배열 생성 방법
- 배열 요소의 참조
- 배열요소 추가와 갱신 및 삭제
- 배열 메서드
- 배열 고차함수(HOC)
해당 포스팅은 4번까지만 보고 5,6번은 따로 포스팅 해놓겠다.
JS에서 배열은 어떤 타입일까?
typeof arr // object
배열은 객체 타입이다. array 타입은 존재하지 않는다.
어라? 배열타입이 없다면 객체랑은 무슨 차이가 있을까?
구분 객체 배열 구조 프로퍼티 키와 프로퍼티 값 인덱스와 요소 값의 참조 프로퍼티 키 인덱스 값의 순서 X O length프로퍼티X O 간단히 표로 나타내면 이런 차이가 있다.
배열(Array) 타입이 없다면 객체로 어떻게 만들어 낸걸까?
자료구조에서 말하는 배열은 동일한 크기의 메모리 공간이 빈틈없이 나열된 자료구조를 말한다.
배열의 요소는 하나의 데이터 타입으로 통일되어 있으며 서로 연속적으로 인접해 있다.
-> 이런 배열을 밀집 배열(dense array)라고 한다.
But! 자바스크립트의 배열은 일반적인 의미의 배열과 다르다.
배열의 요소를 위한 각각의 메모리 공간은 동일한 크기를 갖지 않아도 되며, 연속적으로 이어져 있지 않을 수도 있다.
배열의 요소가 연속적으로 이어져 있지 않은 배열을 희소배열(sparese array)라고 한다.
JS배열은 일반적인 배열의 동작을 흉내 낸 특수한 객체야
예제로 보자
console.log(Object.getOwnPropertyDescriptors([1,2,3]));
/*
{
'0':{value: 1, writable: true, enumerable: true, configurable: true}
'1':{value: 2, writable: true, enumerable: true, configurable: true}
'2':{value: 3, writable: true, enumerable: true, configurable: true}
length: {value: 3, writable: true, enumerable: false, configurable: false}
}
*/
위 예제처럼 배열은 인덱스를 나타내는 문자열을 프로퍼티 키로 가지고, length프로퍼티를 갖는 특수한 객체다.
정리하면, JS배열의 요소는 프로퍼티 값이다.
그러니까 JS에서 사용할 수 있는 모든 값은 객체의 프로퍼티값이 될 수 있기에 어떤 타입의 값이라도 배열의 요소가 될수 있는 것이다.
해보자
const arr = [1,2,3,4,5];
arr.length = 3;
console.log(arr) // [1,2,3]
const arr = [1,2,3];
arr.length = 5;
console.log(arr.length) // 5
console.log(arr) //[1,2,3,empty*2]
이처럼 배열의 일부가 비어있는 배열을 희소배열이라고 한다.
const arr = [, 2, , 4] // 희소배열 length=4, 요소의 개수와 길이는 일치하지 않는다.
희소배열은 JS 엔진상 가능한 시나리오다. 하지만, 일반적인 자료구조에서 말하는 배열과는 많이 다르기에 사용하지 않는게 좋다.
여러 값이 들어갈수 있지만, 배열에는 같은 타입의 요소를 연속적으로 위치시키는 것이 가장좋다.
가장 일반적인 생성방식이다. 두말할거 없이 예제로보자
const arr =[1,2,3]; 이게 끝이다. 대괄호 ([])로 묶으면 끝이다.
Array 생성자 함수로 생성하는건데, 예제로 보자
const arr = new Array(10)//empty*10개의 배열 생성
이처럼, 인자로 정수를 넘겨줬을때, 길이가 10인 배열이 생성된다. 하지만, 내부에는 아무것도 들어있지 않다. >> 아까 배웠던 희소배열이 생성된다.
new Array();// => []인수가 없다면 빈배열이 생성된다. 그냥 리터럴 []과 똑같다.
new Array(1,2,3) // => [1,2,3]2개 이상 전달시, 해당 인수를 요소로 갖는 배열을 생성한다.
new Array({}) // [{}] 1개만 전달할때, 숫자가 아니면 인수를 요소로 갖는 배열을 생성한다.
여기서 new연산자와 함께 호출하지 않더라도 알아서 생성자 함수로 동작한다.
Array 생성자 함수 내에서 new.target을 확인하기 때문이다
ES6에서 도입된 친구다. 전달된 인수를 갯수 상관없이 배열로 생성시켜준다.
Array.of(1); // [1]
Array.of(1,2,3) //[1,2,3]
Array.of('s') //['s']
ES6에서 도입된 친구다. 유사배열객체, 이터러블 객체를 인수로 전달받아서 배열로 변환하여 반환한다.
두가지 예시로 살펴보자
Array.from({length:2, 0:'a', 1:'b'}) //['a','b']
Array.from('Hello') // ['H','e','l','l','o']
문자열도 이터러블이라 변환해서 배열로 만들어주는 모습을 볼수 있다.
배열요소 참조는 대괄호 표기법을 사용해서 접근한다. 뻔한이야기라 희소배열에서 어떻게 적용되는지 알아보자
const arr = [1, ,3];
console.log(arr[1]); //undefined
존재하지 않기에 undefined로 나온다. 메모리 공간도 할당이 안되어있는데 오류가 발생하는 것이 아닌 undefined로 나오기때문에 희소배열이 생기지 않게 주의하자.
const arr = [0];
arr[1]=1; //add value
console.log(arr) //[0,1]
원하는 인덱스 호출 후 추가시 새로운 요소가 추가된다.
여기서 arr[100]=1라고 선언시, 중간에 98개의 빈공간 즉, 희소배열이 되어버린다.
프로퍼티를 추가 할수도 있다.
const arr = [];
arr[0]=1;
arr['foo']=3;
console.log(arr) //[1, foo: 3]
console.log(arr.length) // 1
프로퍼티는 length에 영향을 주지 않는다
delete 연산자(권장하지 않음)
왜 권장하지 않는지 살펴보자.
const arr = [1,2,3];
delete arr[1]
console.log(arr) //[1,empty,3]
//length = 3
이처럼 해당 요소만 삭제, 희소배열이된다.
따라서 원하는 요소를 삭제할땐 splice를 쓰자.
참고로 splice는 시간복잡도 O(n)이다.
메서드와 고차함수는 추후 포스팅에서 따로 다루겠다.