5.28 Array

배열

배열(array)은 1개의 변수에 여러 개의 값을 순차적으로 저장할 때 사용한다. 자바스크립트의 배열은 객체이며 유용한 내장 메소드를 포함하고 있다.

배열은 Array 생성자로 생성된 Array 타입의 객체이며 프로토타입 객체는 Array.prototype이다.

1. 배열의 생성

1.1 배열 리터럴

0개 이상의 값을 쉼표로 구분하여 대괄호([])로 묶는다. 첫번째 값은 인덱스 ‘0’으로 읽을 수 있다. 존재하지 않는 요소에 접근하면 undefined를 반환한다.

var emptyArr = [];

var arr = [
  'zero', 'one', 'two', 'three', 'four',
  'five', 'six', 'seven', 'eight', 'nine'
];

console.log(emptyArr[1]); // undefined
console.log(arr[1]);      // 'one'
console.log(emptyArr.length); // 0
console.log(arr.length);  // 10
console.log(typeof arr);  // object

위의 배열을 객체 리터럴로 유사하게 표현하면 다음과 같다.

var obj = {
  '0': 'zero',  '1': 'one',   '2': 'two',
  '3': 'three', '4': 'four',  '5': 'five',
  '6': 'six',   '7': 'seven', '8': 'eight',
  '9': 'nine'
};

배열 리터럴은 객체 리터럴과 달리 프로퍼티명이 없고 각 요소의 값만이 존재한다. 객체는 프로퍼티 값에 접근하기 위해 대괄호 표기법 또는 마침표 표기법을 사용하며 프로퍼티명을 키로 사용한다. 배열은 요소에 접근하기 위해 대괄호 표기법만을 사용하며 대괄호 내에 접근하고자 하는 요소의 인덱스를 넣어준다. 인덱스는 0부터 시작한다.

두 객체의 근본적 차이는 배열 리터럴 arr의 프로토타입 객체는 Array.prototype이지만 객체 리터럴 obj의 프로토타입 객체는 Object.prototype이라는 것이다.

object prototype & array prototype

객체리터럴과 배열리터럴의 프로토타입

var emptyArr = [];
var emptyObj = {};

console.dir(emptyArr.__proto__);
console.dir(emptyObj.__proto__);

object prototype & array prototype

대부분의 프로그래밍 언어에서 배열의 요소들은 모두 같은 데이터 타입이어야 하지만, 자바스크립트 배열은 어떤 데이터 타입의 조합이라도 포함할 수 있다.

var misc = [
  'string',
  10,
  true,
  null,
  undefined,
  NaN,
  Infinity,
  ['nested array'],
  { object: true },
  function () {}
];

console.log(misc.length); // 10

1.2 Array() 생성자 함수

배열은 일반적으로 배열 리터럴 방식으로 생성하지만 배열 리터럴 방식도 결국 내장 함수 Array() 생성자 함수로 배열을 생성하는 것을 단순화시킨 것이다. Array() 생성자 함수는 Array.prototype.constructor 프로퍼티로 접근할 수 있다.

Array() 생성자 함수는 매개변수의 갯수에 따라 다르게 동작한다.

매개변수가 1개이고 숫자인 경우 매개변수로 전달된 숫자를 length 값으로 가지는 빈 배열을 생성한다.

var arr = new Array(2);
console.log(arr); // (2) [empty × 2]

그 외의 경우 매개변수로 전달된 값들을 요소로 가지는 배열을 생성한다.

var arr = new Array(1, 2, 3);
console.log(arr); // [1, 2, 3]

2. 배열 요소의 추가와 삭제

2.1 배열 요소의 추가

객체가 동적으로 프로퍼티를 추가할 수 있는 것처럼 배열도 동적으로 요소를 추가할 수 있다. 이때 순서에 맞게 값을 할당할 필요는 없고 필요한 인덱스 위치에 값을 할당한다. 값이 할당되지 않은 인덱스 위치의 요소는 생성되지 않는다. 단, 존재하지 않는 요소를 참조하면 undefined가 반환된다. 배열의 길이(length)는 최종 인덱스 위치의 기준으로 산정된다.

var arr = [];
console.log(arr[0]); // undefined

arr[0] = 'one';
arr[3] = 'three';
arr[7] = 'seven';

console.log(arr); // (8) ["one", empty × 2, "three", empty × 3, "seven"]

2.2 배열 요소의 삭제

배열은 객체이기 때문에 배열의 요소를 삭제하기 위해 delete 연산자를 사용할 수 있다. 이때 length에는 변함이 없다. 해당 요소를 완전히 삭제하여 length에도 반영되게 하기 위해서는 Array.prototype.splice 메소드를 사용한다.

var numbersArr = ['zero', 'one', 'two', 'three'];

// 요소의 값만 삭제된다
delete numbersArr[2]; // (4) ["zero", "one", empty, "three"]
console.log(numbersArr);

// 요소 값만이 아니라 요소를 완전히 삭제한다
// splice(시작 인덱스, 삭제할 요소수)
numbersArr.splice(2, 1); // (3) ["zero", "one", "three"]
console.log(numbersArr);

3. 배열 요소의 열거

객체의 프로퍼티를 열거할 때 for in 문을 사용한다. 배열 역시 객체이므로 for in 문을 사용할 수 있다. 그러나 배열은 객체이기 때문에 프로퍼티를 가질 수 있다. for in 문을 사용하면 불필요한 프로퍼티까지 출력될 수 있고 요소들의 순서를 보장하지 않으므로 배열을 열거하는데 적합하지 않다.

따라서 배열 요소의 열거에는 forEach 메소드 또는 for 문을 사용하는 것이 좋다.

var arr = ['zero', 'one', 'two', 'three'];
arr.foo = 10;

for (var prop in arr) {
  console.log(prop, arr[prop]);
  // 0 zero / 1 one / 2 two / 3 three / foo 10
}

arr.forEach((item, i) => console.log(i, item));
// => 0 'zero' / 1 'one' / 2 'two' / 3 'three'

for (var i = 0; i < arr.length; i++) {
  console.log(i, arr[i]);
}
// => 0 'zero' / 1 'one' / 2 'two' / 3 'three'

4. Array Property

4.1 Array.length

length 프로퍼티는 요소의 개수(배열의 길이)를 나타낸다. 배열 인덱스는 32bit 양의 정수로 처리된다. 따라서 length 프로퍼티의 값은 양의 정수이며 232 - 1(4,294,967,296 - 1) 미만이다.

var arr = [1, 2, 3, 4, 5];
console.log(arr.length); // 5

arr[4294967294] = 100;
console.log(arr.length); // 4294967295
console.log(arr); // (4294967295) [1, 2, 3, 4, 5, empty × 4294967289, 100]

arr[4294967295] = 1000;
console.log(arr.length); // 4294967295
console.log(arr); // (4294967295) [1, 2, 3, 4, 5, empty × 4294967289, 100, 4294967295: 1000]

주의할 것은 배열에 요소의 개수와 length 프로퍼티의 값이 반드시 일치하지는 않는다는 것이다.

배열에 요소의 개수와 length 프로퍼티의 값이 일치하지 않는 배열을 희소 배열(sparse array)이라 한다. 희소 배열은 배열의 요소가 연속적이지 않은 배열을 의미한다. 희소 배열이 아닌 일반 배열은 배열의 요소 개수와 length 프로퍼티의 값이 언제나 일치하지만 희소 배열은 배열의 요소 개수보다 length 프로퍼티의 값이 언제나 크다. 희소 배열은 일반 배열보다 느리며 메모리를 낭비한다.

현재 length 프로퍼티 값보다 더 큰 인덱스로 요소를 추가하면 새로운 요소를 추가할 수 있도록 자동으로 length 프로퍼티의 값이 늘어난다. length 프로퍼티의 값은 가장 큰 인덱스에 1을 더한 것과 같다.

var arr = [];
console.log(arr.length); // 0

arr[1000] = true;

console.log(arr);        // (1001) [empty × 1000, true]
console.log(arr.length); // 1001
console.log(arr[0]);     // undefined

length 프로퍼티의 값은 명시적으로 변경할 수 있다. 만약 length 프로퍼티의 값을 현재보다 작게 변경하면 변경된 length 프로퍼티의 값보다 크거나 같은 인덱스에 해당하는 요소는 모두 삭제된다.

var arr = [ 1, 2, 3, 4, 5 ];

// 배열 길이의 명시적 변경
arr.length = 3;
console.log(arr); // (3) [1, 2, 3]

5. Array Method

  • ✏️ 메소드는 this(원본 배열)를 변경한다.
  • 🔒 메소드는 this(원본 배열)를 변경하지 않는다.

5.1 Array.isArray(arg: any): boolean ES5

객체가 배열이면 true, 배열이 아니면 false를 반환한다.

// true
Array.isArray([]);
Array.isArray([1, 2]);
Array.isArray(new Array());

// false
Array.isArray();
Array.isArray({});
Array.isArray(null);
Array.isArray(undefined);
Array.isArray(1);
Array.isArray('Array');
Array.isArray(true);
Array.isArray(false);

5.2 Array.prototype.indexOf(searchElement: T, fromIndex?: number): number 🔒 ES5

indexOf 메소드의 인자로 지정된 요소를 배열에서 검색하여 인덱스를 반환한다. 중복되는 요소가 있는 경우 첫번째 인덱스만 반환된다. 만일 해당하는 요소가 없는 경우, -1을 반환한다.

var arr = [1, 2, 2, 3];
console.log(arr.indexOf(2));    // 1
console.log(arr.indexOf(4));    // -1
console.log(arr.indexOf(2, 2)); // 1

5.3 Array.prototype.concat(…items: Array<T[] | T>): T[] 🔒 ES3

concat 메소드의 인수로 넘어온 값들(배열 또는 값)을 자신의 복사본에 요소로 추가하고 반환한다. 이때 원본 배열은 변경되지 않는다.

var a = ['a', 'b', 'c'];
var b = ['x', 'y', 'z'];

var c = a.concat(b);
console.log(c); // ['a', 'b', 'c', 'x', 'y', 'z']

var d = a.concat('String');
console.log(d); // ['a', 'b', 'c', 'String']

var e = a.concat(b, true);
console.log(e); // ['a', 'b', 'c', 'x', 'y', 'z', true]

// 원본 배열은 변하지 않는다.
console.log(a); // [ 'a', 'b', 'c' ]

5.4 Array.prototype.join(separator?: string): string 🔒 ES1

배열 요소 전체를 연결하여 생성한 문자열을 반환한다. 구분자(separator)는 생략 가능하며 기본 구분자는 ,이다.

Array.prototype.join() 메소드는 + 연산자보다 빠르다.

var arr = ['a', 'b', 'c', 'd'];

var x = arr.join();
console.log(x);  // 'a,b,c,d';

var y = arr.join('');
console.log(y);  // 'abcd'

var z = arr.join(':');
console.log(z);  // 'a:b:c:d'

5.5 Array.prototype.pop(): T | undefined ✏️ ES3

배열에서 마지막 요소를 제거하고 제거한 요소를 반환한다. 만약 빈 배열일 경우 undefined를 반환한다. pop 메소드는 대상 배열 자체를 변경한다.

var a = ['a', 'b', 'c'];
var c = a.pop();

// 원본 배열이 변경된다.
console.log(a); // a --> ['a', 'b']
console.log(c); // c --> 'c'

poppush와 함께 배열을 스택(LIFO: Last In First Out)처럼 동작하게 한다.

var arr = [];

arr.push(1); // [1]
arr.push(2); // [1, 2]
arr.push(3); // [1, 2, 3]

arr.pop(); // [1, 2]
arr.pop(); // [1]
arr.pop(); // []

5.6 Array.prototype.push(…items: T[]): number ✏️ ES3

인자로 전달된 항목을 배열의 마지막에 추가한다. concat 메소드와 다르게 인자로 전달된 항목을 마지막 요소로 추가한다. 반환값은 배열의 새로운 length 값이다. push 메소드는 대상 배열 자체를 변경한다.

var a = ['a', 'b', 'c'];
var b = ['x', 'y', 'z'];

// push는 원본 배열을 직접 변경하고 변경된 배열의 length를 반환한다.
var c = a.push(b);
console.log(a); // a --> ['a', 'b', 'c', ['x', 'y', 'z']]
console.log(c); // c --> 4;

// concat은 원본 배열을 직접 변경하지 않고 복사본을 반환한다.
console.log([1, 2].concat([3, 4])); // [ 1, 2, 3, 4 ]

배열의 마지막에 값을 추가 할 때는 Array.prototype.push, 선두에 추가 할 때는 Array.prototype.unshift, 중간에 추가할 때는 Array.prototype.splice 메소드를 사용한다.

단, push, unshift 메소드는 사용하기 간편하나 performance 면에서는 좋은 방법은 아니다.

var arr = [1, 2, 3, 4, 5];

arr.push(6);
arr[arr.length] = 6; // 43% faster in Chrome 47.0.2526.106 on Mac OS X 10.11.1

arr.unshift(0);
[0].concat(arr); // 98% faster in Chrome 47.0.2526.106 on Mac OS X 10.11.1

5.7 Array.prototype.reverse(): this ✏️ ES1

배열 요소의 순서를 반대로 변경한다. 이때 원본 배열이 변경된다. 반환값은 변경된 배열이다.

var a = ['a', 'b', 'c'];
var b = a.reverse();

// 원본 배열이 변경된다
console.log(a); // [ 'c', 'b', 'a' ]
console.log(b); // [ 'c', 'b', 'a' ]

5.8 Array.prototype.shift(): T | undefined ✏️ ES3

배열에서 첫요소를 제거하고 제거한 요소를 반환한다. 만약 빈 배열일 경우 undefined를 반환한다. shift 메소드는 대상 배열 자체를 변경한다.

var a = ['a', 'b', 'c'];
var c = a.shift();

// 원본 배열이 변경된다.
console.log(a); // a --> [ 'b', 'c' ]
console.log(c); // c --> 'a'

shiftpush와 함께 배열을 큐(FIFO: First In First Out)처럼 동작하게 한다.

var arr = [];

arr.push(1); // [1]
arr.push(2); // [1, 2]
arr.push(3); // [1, 2, 3]

arr.shift(); // [2, 3]
arr.shift(); // [3]
arr.shift(); // []

Array.prototype.pop()은 마지막 요소를 제거하고 제거한 요소를 반환한다.

var a = ['a', 'b', 'c'];
var c = a.pop();

// 원본 배열이 변경된다.
console.log(a); // a --> ['a', 'b']
console.log(c); // c --> 'c'

array-method

5.9 Array.prototype.slice(start=0, end=this.length): T[] 🔒 ES3

인자로 지정된 배열의 부분을 복사하여 반환한다. 원본 배열은 변경되지 않는다.

첫번째 매개변수 start에 해당하는 인덱스를 갖는 요소부터 매개변수 end에 해당하는 인덱스를 가진 요소 전까지 복사된다.

  • 매개변수
start
음수인 경우 배열의 끝에서의 인덱스를 나타낸다. 예를 들어 slice(-2)는 배열의 마지막 2개의 요소를 반환한다.
end
옵션이며 기본값은 length 값이다.
var items = ['a', 'b', 'c'];

// items[0]부터 items[1] 이전(items[1] 미포함)까지 반환
var res1 = items.slice(0, 1);
console.log(res1);  // [ 'a' ]

// items[1]부터 items[2] 이전(items[2] 미포함)까지 반환
var res2 = items.slice(1, 2);
console.log(res2);  // [ 'b' ]

// items[1]부터 이후의 모든 요소 반환
var res3 = items.slice(1);
console.log(res3);  // [ 'b', 'c' ]

// 인자가 음수인 경우 배열의 끝에서 2개의 요소를 반환
var res4 = items.slice(-2);
console.log(res4);  // [ 'b', 'c' ]

// 모든 요소를 반환 (= 복사본 생성)
var res5 = items.slice();
console.log(res5);  // [ 'a', 'b', 'c' ]

// 원본은 변경되지 않는다.
console.log(items); // [ 'a', 'b', 'c' ]

slice

Array.prototype.slice 메소드

slice 메소드에 인자를 전달하지 않으면 원본 배열의 복사본을 생성하여 반환한다.

var arr = [1, 2, 3];
var copy = arr.slice();
console.log(copy, copy === arr); // [ 1, 2, 3 ] false

이를 이용하여 arguments, HTMLCollection, NodeList와 같은 유사 배열 객체(Array-like Object)를 배열로 변환할 수 있다.

function sum() {
  // 유사 배열 객체 => Array
  var arr = Array.prototype.slice.call(arguments);
  console.log(arr); // [1, 2, 3]

  return arr.reduce(function (pre, cur) {
    return pre + cur;
  });
}

console.log(sum(1, 2, 3));

ES6에서 유사 배열 객체를 배열로 변환하는 방법은 아래와 같다.

// 유사 배열 객체 => Array
function sum() {
  ...
  // Spread 연산자
  var arr = [...arguments];
  // Array.from 메소드는 유사 배열 객체를 복사하여 배열을 생성한다.
  var arr = Array.from(arguments);
  ...
}

5.10 Array.prototype.splice(start: number, deleteCount=this.length-start, …items: T[]): T[] ✏️ ES3

기존의 배열의 요소를 제거하고 그 위치에 새로운 요소를 추가한다. 배열 중간에 새로운 요소를 추가할 때도 사용된다.

  • 매개변수
start
배열에서의 시작 위치이다
deleteCount
시작 위치(start)부터 제거할 요소의 수이다.
items
삭제한 위치에 추가될 요소들이다. (옵션)
  • 반환값 삭제한 요소들을 가진 배열이다.

이 메소드의 가장 일반적인 사용은 배열에서 요소를 삭제할 때다.

var items = [1, 2, 3, 4];

// items[1]부터 2개의 요소를 제거하고 제거된 요소를 배열로 반환
var res = items.splice(1, 2);

// 원본 배열이 변경된다.
console.log(items); // [ 1, 4 ]
// 제거한 요소가 배열로 반환된다.
console.log(res);   // [ 2, 3 ]

splice Array.prototype.splice 메소드

배열에서 요소를 제거하고 제거한 위치에 다른 요소를 추가한다.

var items = [1, 2, 3, 4];

// items[1]부터 2개의 요소를 제거하고 그자리에 새로운 요소를 추가한다. 제거된 요소가 반환된다.
var res = items.splice(1, 2, 20, 30);

// 원본 배열이 변경된다.
console.log(items); // [ 1, 20, 30, 4 ]
// 제거한 요소가 배열로 반환된다.
console.log(res);   // [ 2, 3 ]

splice Array.prototype.splice 메소드

배열 중간에 새로운 요소를 추가할 때도 사용된다.

var items = [1, 2, 3, 4];

// items[1]부터 0개의 요소를 제거하고 그자리(items[1])에 새로운 요소를 추가한다. 제거된 요소가 반환된다.
var res = items.splice(1, 0, 100);

// 원본 배열이 변경된다.
console.log(items); // [ 1, 100, 2, 3, 4 ]
// 제거한 요소가 배열로 반환된다.
console.log(res);   // [ ]

배열 중간에 배열의 요소들을 해체하여 추가할 때도 사용된다.

var items = [1, 4];

// items[1]부터 0개의 요소를 제거하고 그자리(items[1])에 새로운 배열를 추가한다. 제거된 요소가 반환된다.
// items.splice(1, 0, [2, 3]); // [ 1, [ 2, 3 ], 4 ]
Array.prototype.splice.apply(items, [1, 0].concat([2, 3]));
// ES6
// items.splice(1, 0, ...[2, 3]);

console.log(items); // [ 1, 2, 3, 4 ]

Reference

Back to top
Close