배열은 많은 양의 '순서가 있는' 데이터를 다루는 데 유용하다. 이러한 부분은 키의 의미는 중요하지만, 순서가 없는 객체와의 차이점으로, 배열은 객체와 달리 원하는 위치에 동적으로 값을 추가할 수 있어 매우 편리하다.
배열은 선언 시에 그 크기를 미리 정할 필요 없이 원하는 값을 추가하면 된다.
let arr = new Array();
let arr = [1, 2, 3];
일반적으로 두 번째 방법으로 많이 선언하며, 선언 시에 값을 포함하여 선언할 수 있다. 배열은 대괄호([])를 사용하는 것이 특징으로 이는 중괄호({})를 사용하는 객체와의 차이점이다.
배열은 다음 방법을 통해 원하는 값을 쉽게 추가할 수 있다.
arr[3] = 4;
arr[0] = 1;
length 프로퍼티는 배열의 길이를 나타낸다.
let fruits = ["사과", "오렌지", "바나나"];
alert( fruits.length ); // 3
length 값을 수동으로 줄이면 배열 끝이 잘리게 된다.
let arr = [1, 2, 3, 4, 5];
arr.length = 2; // 요소 2개만 남기고 자르기
alert( arr ); // [1, 2]
arr.length = 5; // 본래 길이로 되돌려 본다
alert( arr[3] ); // undefined: 복구되지 않음
arr.length = 0; // 배열 모두 비우기
이러한 length 프로퍼티는 배열 외에도 객체에서 가질 수 있는데, 이러한 객체를 '유사 배열 객체(array-like objects)라고 부른다. 유사 배열 객체의 특징은 객체이지만 배열 메소드를 사용할 수 있다는 점이다.
여기서 주의할 점은 배열에 값을 추가할 때 객체와 같이 문자열 키를 사용하더라도 배열은 숫자를 사용한 것과 같이 작동한다!
let arr = [1, 2, 3];
alert( arr.length ); // 3
arr['13'] = 13;
alert( arr.length ); // 14
따라서 혼동을 유발하는 문자열 타입의 키를 사용하는 것은 추천하지 않는다.
배열은 실제로는 내장된 Array객체를 이용해 표현되며, 다음과 같은 특정 메소드를 사용할 수 있다.
let arr = ["a", "b", "c"];
arr.push("d"); // ["a", "b", "c", "d"]
alert( arr.length ); // 4
alert( arr.push("e") ); // 5
push( item )
메소드는 배열 끝에 요소 item을 추가한다. 위 예시와 같이 arr.push( ) 자체를 출력하면 추가된 배열의 length가 출력되는 것을 볼 수 있다.
arr.pop(); // ["a", "b", "c", "d"]
alert( arr.length ) ; // 4
alert( arr.pop() ) ; // "d"
pop()
메소드는 배열 끝의 한 요소를 제거한다. arr.pop( ) 자체를 출력하면 push()
와는 다르게 제거한 요소가 출력되는 것을 볼 수 있다.
arr.shift(); // ["a", "b"]
alert( arr.length ); // 2
alert( arr.shift() ); // "b"
shift()
메소드는 배열의 처음 요소 하나를 제거한다. pop()과 마찬가지로 그 자체를 출력하면 제거한 요소를 반환한다.
arr.unshift("b", "c"); // ["a", "b", "c"]
alert( arr.length ); // 3
alert( arr.unshift("d") ); // 4
unshift( item )
메소드는 배열의 맨 앞에 item을 추가한다. push()와 마찬가지로 그 자체를 출력하면 추가된 배열의 length를 반환한다. 또한, 위 예시와 같이 push()
와 unshift()
메소드는 여러 요소를 추가하는 것 또한 가능하다.
여기서 주의할 점은 pop과 push는 배열의 맨 뒤에서 수정이 이루어지기 때문에 비교적 빠른 반면, shift와 unshift는 배열의 앞 요소를 수정하기 때문에 뒤에 오는 모든 배열 위치를 변화시키느라 관련 연산이 늘어나고 느리다는 점이다.
let arr1 = [1, 2, 3];
let arr2 = [4, 5, 6];
arr1.concat(arr2); // [1, 2, 3, 4, 5, 6]
concat()
메소드는 두 배열을 하나로 합친다.
let arr = ["a", "b", "c"];
arr.join("/"); // "a/b/c"
join()
메소드는 원하는 값을 배열 사이에 삽입한다. 아무런 값을 넣지 않으면 배열 요소를 모두 합쳐서 보여준다. 주의할 점은 출력 결과가 배열이 아니라는 점이다.
slice(start[, end])
메소드의 경우 특이한 점이 있는데 바로 배열을 복사해 새로운 배열로 반환한다는 점이다.
let arr = [1, 2, 3];
alert(arr.slice(2)); // [3]
alert(arr); // [1, 2, 3]
위 예시와 같이 slice() 메소드 자체는 특정한 부분을 반환하지만, 원본 arr을 출력하면 값이 변하지 않는다. 이는 slice() 메소드가 원본값을 바꾸지 않고 복사해서 출력하기 때문이다. 이러한 특성은 원본을 변화시키지 않기 때문에 immutable 하다고 부른다. immutable 한 메소드의 다른 예로는 filter와 map이 있으며, 이는 고차함수 파트에서 자세히 다룰 것이다. immutable의 개념은 메소드 뿐만 아니라 타입에서도 적용될 수 있는데, 대표적인 immutable 타입은 문자열과 같은 원시 타입이다.
let arr = [1, 2, 3];
alert(arr.splice(2, 1)); // [3]
alert(arr); // [1, 2]
slice()
와 비슷한 역할을 하는 메소드 splice(start[, deleteCount[, item1]])
는 이와 반대로 원본을 직접 수정하는 mutable 한 메소드이다. 위 사례에서 마지막에 arr을 출력하면 3이 사라진 것을 볼 수 있다.