객체: 키를 사용해 식별할 수 있는 값을 담은 컬렉션.
그러나 순서가 있는 컬렉션이 필요할 때가 있다.
순서가 있는 컬렉션을 저장할 때 배열을 사용하자.
두 문법을 사용하면 빈 배열을 사용할 수 있다.
let arr = new Array();
let arr = [];
대부분 두 번째 방법으로 배열을 선언한다.
이때 대괄호 안에 초기 요소를 넣어줄 수 있다.
각 배열 요소에는 0부터 시작하는 숫자(인덱스)가 매겨져 있다. 이 숫자들은 배열 내 순서를 나타낸다.
let fruits = ["사과", "오렌지", "자두"];
alert( fruits[0] ); // 사과
alert( fruits[1] ); // 오렌지
alert( fruits[2] ); // 자두
fruits[2] = '배'; // 배열이 ["사과", "오렌지", "배"]로 바뀜
fruits[3] = '레몬'; // 배열이 ["사과", "오렌지", "배", "레몬"]으로 바뀜
length
를 사용하면 배열에 담긴 요소가 몇 개인지 알 수 있다.let fruits = ["사과", "오렌지", "자두"];
alert( fruits.length ); // 3
alert
를 사용하면 요소 전체를 출력하는 것도 가능하다.let fruits = ["사과", "오렌지", "자두"];
alert( fruits ); // 사과,오렌지,자두
큐(queue) : 배열을 사용해 만들 수 있는 대표적인 자료구조
배열에는 두 연산을 가능케 하는 내장 메서드 push와 pop이 있다.
스택에서 사용하는 연산은 다음과 같다.
스택: 한쪽 끝에 요소를 더하거나 뺄 수 있게 해주는 자료구조
자바스크립트 배열을 사용하면 큐와 스택 둘다 만들 수 있다.
데큐: 처음이나 끝에 요소를 더하거나 빼주는 연산을 제공하는 자료구조
배열 끝에 무언가를 해주는 메서드들
let fruits = ["사과", "오렌지", "배"];
alert( fruits.pop() ); // 배열에서 "배"를 제거하고 제거된 요소를 얼럿창에 띄웁니다.
alert( fruits ); // 사과,오렌지
let fruits = ["사과", "오렌지"];
fruits.push("배");
alert( fruits ); // 사과,오렌지,배
배열 앞에 무언가를 해주는 메서드
let fruits = ["사과", "오렌지", "배"];
alert( fruits.shift() ); // 배열에서 "사과"를 제거하고 제거된 요소를 얼럿창에 띄웁니다.
alert( fruits ); // 오렌지,배
let fruits = ["오렌지", "배"];
fruits.unshift('사과');
alert( fruits ); // 사과,오렌지,배
let fruits = ["사과"];
fruits.push("오렌지", "배");
fruits.unshift("파인애플", "레몬");
// ["파인애플", "레몬", "사과", "오렌지", "배"]
alert( fruits );
배열은 키가 숫자라는 점이 다르다.
숫자형 키를 사용하기 때문에 객체 기본 기능 이외에도 순서가 있는 컬렉션을 제어하게 해주는 특별한 메서드를 제공한다.
그러나 배열의 본질은 객체이다.
그런데 개발자가 배열을 '순서가 있는 자료의 컬렉션'처럼 다루지 않고 일반 객체처럼 다루면 기법들이 제대로 동작하지 않는다.
let fruits = []; // 빈 배열을 하나 만듭니다.
fruits[99999] = 5; // 배열의 길이보다 훨씬 큰 숫자를 사용해 프로퍼티를 만듭니다.
fruits.age = 25; // 임의의 이름을 사용해 프로퍼티를 만듭니다.
이렇게 코드를 작성하면 자바스크립트 엔진이 배열을 일반 객체처럼 다루게 되어 배열을 다룰 때만 적용되는 최적화 기법이 동작하지 않아 배열 특유의 이점이 사라진다.
잘못된 사용 예시:
push와 pop은 빠르지만 shift와 unshift는 느리다.
shift와 unshift 는 배열에 요소가 많으면 요소가 이동하는 데 걸리는 시간이 길고 메모리 관리 연산도 많아진다.
그러나 pop과 push 메소드는 요소를 옮기지 않기 때문에 각 요소는 기존 인덱스를 그대로 유지한다. 배열 끝에 무언가를 해주는 메서드의 실행 속도가 항상 이렇기 때문에 빠르다.
for문은 배열을 순회할 때 사용한다.
let arr = ["사과", "오렌지", "배"];
for (let i = 0; i < arr.length; i++) {
alert( arr[i] );
}
배열에 적용할 수 있는 또 다른 순회 문법으로는 for..of 가 있다.
let fruits = ["사과", "오렌지", "자두"];
// 배열 요소를 대상으로 반복 작업을 수행합니다.
for (let fruit of fruits) {
alert( fruit );
}
for..of 를 사용하면 현재 요소의 인덱스는 얻을 수 없고 값만 얻을 수 있다.
for... in을 사용하는 것 또한 가능하다.
let arr = ["사과", "오렌지", "배"];
for (let key in arr) {
alert( arr[key] ); // 사과, 오렌지, 배
}
for..in의 주의점은 모든 프로퍼티를 대상으로 순회하고, 배열이 아니라 객체와 함께 사용할 때 최적화되어있다는 특징이 존재한다.
배열에 무언가 조작을 가하면 length 프로퍼티가 자동으로 갱신된다.
length 프로퍼티: 배열 내 요소의 개수가 아니라 가장큰 인덱스에 1을 더한 값
let fruits = [];
fruits[123] = "사과";
alert( fruits.length ); // 124
let arr = [1, 2, 3, 4, 5];
arr.length = 2; // 요소 2개만 남기고 잘라봅시다.
alert( arr ); // [1, 2]
arr.length = 5; // 본래 길이로 되돌려 봅시다.
alert( arr[3] ); // undefined: 삭제된 기존 요소들이 복구되지 않습니다.
new Array() 문법을 사용해서 배열을 만들면 실수가 유발될 수 있다.
let arr = new Array(2); // 이렇게 하면 배열 [2]가 만들어질까요?
alert( arr[0] ); // undefined가 출력됩니다. 요소가 하나도 없는 배열이 만들어졌네요.
alert( arr.length ); // 길이는 2입니다.
이러한 실수를 하지 않기 위해 대부분의 개발자가 대괄호를 써서 배열을 만든다.
다차원 배열: 배열 역시 배열의 요소가 될 때
let matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];
alert( matrix[1][1] ); // 5, 중심에 있는 요소
배열에는 toString 메서드가 구현돼서 이를 호출하면 요소를 쉼표로 구분한 문자열이 반환된다.
let arr = [1, 2, 3];
alert( arr ); // 1,2,3
alert( String(arr) === '1,2,3' ); // true