자바스크립트의 배열

자바스크립트의 배열 활용하기

저~~번에 살펴봤듯이 배열은 Array 타입의 객체 이며 Array.prototype의 프로퍼티를 상속받는다. 오늘은 Array.prototype 객체에 저장되어 있는 배열 메서드를 활용하는 방법에 대해 알아보려고 한다.

수정 메서드

수정 메서드는 원본 배열을 바로 수정해버리기 때문에 주의가 필요하다.

  • push 메서드

배열 마지막에 하나 이상의 요소를 추가한 후 그 배열의 길이를 반환한다.

1
2
var a = ['A','B','C'];
a.push('D');

두 개 이상 추가 시에는 요소의 값을 쉼표로 구분하여 인수로 넘긴다.

  • pop 메서드

배열의 마지막 요소를 잘라내어 반환한다.

1
2
var a = ['A','B','C'];
a.pop();
  • shift 메서드

배열의 첫 번째 요소를 제거한 후 모든 배열 요소를 왼쪽으로 이동시킨다. 반환값은 삭제된 요소의 값이다.

1
2
var a = ['A','B','C'];
a.shift();
  • unshift 메서드

배열 앞부분에 요소를 한 개 이상 추가한 후 배열 요소를 모두 오른쪽으로 이동시킨다. 반환값을 배열의 길이이다.

1
2
var a = ['A','B','C'];
a.unshift("X");
  • splice 메서드

특정 인덱스의 배열 요소를 갈아 끼우거나 삭제할 때 사용한다.

Array.prototype.splice(index, howmany [,data …]);

1
2
var a = ['A','B','C','D'];
a.splice(1, 2, 'X','Y','Z'); // 인덱스 1부터 2개를 삭제하고 그 위에 생성된 요소를 끼워 넣는다.

첫 번째 인수만 넘기면 그 인덱스 이후에 있는 모든 배열 요소를 삭제한다. 또한 첫 번째 인수가 음수이면 이 값에 배열 길이를 더한 값을 삭제 시작위치로 간주한다.

두 번째 인수가 0일 경우 인덱스가 가리키는 요소 바로 앞에 세 번째 이후 요소를 끼워 넣는다.

  • sort 메서드

배열 안의 요소를 정렬하는 메서드이다. 인수는 실제 비교를 담당하는 함수의 참조를 넘기고 반환값을 정렬된 배열이다.

1
2
3
4
var points = [40, 100, 1, 5, 2, 25, 10];

points.sort();
console.log(points); // [ 1, 10, 100, 2, 25, 40, 5 ]

주의할 점은 배열의 요소가 숫자여도 일시적으로 문자열로 변환된다는 사실이다. 그래서 10이 2보다 앞서게 되는 상황이 발생하게 된다. (유니코드)

그래서 그런 경우를 방지하기 위해 밑의 예제처럼 사용하는 공식이 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var points = [40, 100, 1, 5, 2, 25, 10];

// 숫자 배열 오름차순 정렬
// 비교 함수의 반환값이 0보다 작은 경우, a를 우선하여 정렬한다.
points.sort(function (a, b) { return a - b; });
console.log(points); // [ 1, 2, 5, 10, 25, 40, 100 ]

// 숫자 배열에서 최소값 취득
console.log(points[0]); // 1

// 숫자 배열 내림차순 정렬
// 비교 함수의 반환값이 0보다 큰 경우, b를 우선하여 정렬한다.
points.sort(function (a, b) { return b - a; });
console.log(points); // [ 100, 40, 25, 10, 5, 2, 1 ]

// 숫자 배열에서 최대값 취득
console.log(points[0]); // 100

또한 undefined는 배열의 마지막에 위치시킨다.
예제를 참조하여 프로퍼티 이름을 key값으로 받아 key가 가리키는 프로퍼티를 기준으로 객체 배열을 정렬해 보자.

접근자 메서드

접근자 메서드는 새로운 배열을 반환하여 원래 배열을 수정하지 않는 메서드이다. 사실 상 가장 많이 사용되는 배열 메서드이다.

  • join 메서드

join은 배열의 모든 요소 값을 문자열로 바꾼 후에 인수로 받은 문자로 연결하여 반환한다. undefined나 null은 빈 문자열로 간주한다.

  • concat 메서드

concat은 인수로 받은 값을 그 배열의 요소로 추가하여 새로운 배열을 생성한다. 참고로 원시값도 배열에 추가할 수 있다.

1
2
3
4
5
var a = ['a', 'b', 'c'];
var b = ['x', 'y', 'z'];

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

일부 요소를 제거한 새로운 배열을 반환하는 메서드이다.

첫 번째 인수는 요소를 꺼낼 시작 위치를, 두 번째 인수는 요소를 꺼낼 마지막 위치를 지정한다.

1
2
3
4
var items = ['a', 'b', 'c'];

var res1 = items.slice(0,1);
console.log(res1);

두 번째 인수를 생략하면 마지막 요소까지 잘라내며 음수는 끝에서 n번째 라는 의미를 갖는다.

  • indexOf와 lastIndexOf 메서드

두 메서드는 배열 안에서 인수로 지정한 값을 검색해서 가장 먼저 찾은 요소의 인덱스를 반환한다. 찾지 못하면 -1을 반환한다. 첫 번째 인수는 검색할 값을 지정하고 두 번째 인수에는 검색을 시작할 인덱스를 지정한다. 생략하면 0으로 간주한다.

1
2
3
4
var arr = [1, 2, 2, 3];
console.log(arr.indexOf(2)); // 1
console.log(arr.indexOf(4)); // -1
console.log(arr.indexOf(2, 2)); // 2
  • toString과 toLocaleString 메서드

배열의 요소를 문자열로 변환하여 쉼표로 연결한 문자열을 반환한다. toLocaleString은 해당 지역에 맞는 언어로 지역화한 문자열로 변환한다.

1
2
3
var date = new Date();
console.log(['a','b','c',date].toString());
console.log(['a','b','c',date].toLocaleString());

반복 메서드

반복 메서드는 모든 요소를 순회하며 특정한 작업을 수행하거나 특정 조건을 만족하는 요소를 가져올 때 사용한다. 반복 메서드의 인수로 전달한 함수는 배열의 요소마다 호출된다.

  • forEach 메서드

Array.prototype.forEach(callback: (value: T, index: number, array: T[]) => void, thisArg?: any): void
인수로 받은 함수를 배열의 요소별로 한 번씩 실행한다.

1
2
3
4
5
6
7
8
var total = 0;
var testArray = [1, 3, 5, 7, 9];

// forEach 메소드는 원본 배열을 변경하지 않는다.
testArray.forEach(function (item, index, array) {
console.log('[' + index + '] = ' + item);
total += item;
});

함수형 프로그래밍을 할 때 for문 대신 사용할 수 있다. 하지만 for문에 비해 성능이 좋지는 않다.

또한 두 번째 인자로 this를 전달할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function Counter() {
this.sum = 0;
this.count = 0;
}

Counter.prototype.add = function (array) {
// entry는 array의 배열 요소의 값
array.forEach(function (entry) {
this.sum += entry; // 2번째 인자 this를 전달하지 않으면 this === window
this.count++;
}, this);
};

var counter = new Counter();
counter.add([2, 5, 9]);
console.log(counter.count);
console.log(counter.sum);
  • map 메서드

인수로 받은 함수를 배열의 요소별로 한번씩 실행하는 데 그 콜백함수의 반환값으로 새로운 배열을 생성한다. 반드시 값을 반환해야 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
var a = [1, 4, 9];

// 배열을 순회하며 각 요소에 대하여 인자로 주어진 콜백함수를 실행
var roots = a.map(function (item) {
return Math.sqrt(item);
});

// 위 코드의 축약표현은 아래와 같다.
// var roots = numbers.map(Math.sqrt);

console.log(roots); // [ 1, 2, 3 ]
// map 메소드는 원본 배열은 변경하지 않는다
console.log(a); // [ 1, 4, 9 ]

map의 반환값은 원본 배열과 같은 개수의 원소가 들어 있는 배열이기 때문에 메서드 체인으로 연결해서 배열 처리가 가능하다.

  • reduce

배열의 첫 번째 요소부터 마지막 요소까지 합성 곱 처리를 한다.(배열 요소 하나를 함수로 처리한 후 그 반환값을 다음 번 요소를 처리할 때 함수의 입력값으로 사용)
그래서 마지막 요소를 처리한 함수가 값을 반환한다.

Array.prototype.reduce(callback: (state: U, element: T, index: number, array: T[]) => U, firstState?: U): U

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var arr = [1, 2, 3, 4, 5];

/*
previousValue: 이전 콜백의 반환값
currentValue : 배열 요소의 값
currentIndex : 인덱스
array : 순회할 배열
*/
var sum = arr.reduce(function (previousValue, currentValue, currentIndex, array) {
console.log(previousValue + '+' + currentValue + '=' + (previousValue + currentValue));
return previousValue + currentValue; // 결과는 다음 콜백의 첫번째 인자로 전달된다
});

console.log(sum);

// 배열의 최대값.
var max = arr.reduce(function (prev, cur) {
return prev > cur ? prev : cur;
});

console.log(max); // 5: 최대값

주의할 점은 초깃값 지정 여부에 따라 callback 함수가 처음 호출될 때 인수로 들어오는 값이 바뀌는데 지정했을 경우에는 state는 초깃값, element는 배열의 첫 번째 요소, index는 0이 된다.
지정하지 않을 경우에는 state는 배열의 첫 번째 요소의 값, element는 두 번째 요소의 값, index는 1이 된다.

[예제 살펴보기]

유사 배열 객체

배열은 아니지만 배열로 처리할 수 있는 개체를 유사 배열 객체라고 한다.

Arguments객체와 DOM의 getElementByTagName, getElementByName이 반환하는 Nodelist, String등이 유사 배열 객체이다.

1
2
3
4
5
6
7
var a = {};
for (var i = 0; i < 10; i++) {
a[i] = i;
}
a.length = 10;
for (var i = 0, sum = 0; i<a.length; i++) sum += a[i];
console.log(sum);

그래서 Array.prototype.call 메서드를 이용하여 메서드들을 간접 호출할 수 있다. 하지만 concat 메서드를 제외한 나머지는 배열처럼 동작하지는 않는다.

ES6

  • 비구조화 할당

우리가 디스트럭쳐링이라고 부르는 것이 바로 비구조화 할당이다. 구조화 되어 있는 배열 혹은 객체를 파괴하여 개별적인 변수에 할당하는 것이다.

  • 배열 디스트럭쳐링
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// ES5

var arr = [1,2,3];

var one = arr[0];
var two = arr[1];
var three = arr[2];

console.log(one, two, three);


// ES6

const arr = [1,2,3];

const [one,two,three] = arr;

console.log(one, two, three);

배열 디스트럭쳐링을 위해서는 할당 연산자 왼쪽에 배열 형태의 변수 리스트가 있어야 한다. 이 때 배열의 인덱스를 기준으로 할당된다.

  • 객체 디스트럭쳐링

ES5에선 객체의 프로퍼티 이름을 활용하여야 변수에 할당이 가능했다. ES6에서는 객체의 각 프로퍼티를 객체로부터 추출하여 변수 리스트에 할당할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// ES5

var obj = {firstName: 'Geun ho', lastName: 'Jang'};

var firstName = obj.firstName;
var lastName = obj.lastName;

console.log(firstName, lastName);

// ES6

const obj = {firstName: 'Geun ho', lastName: 'Jang'};

const { firstName, lastName } = obj;

console.log(firstName, lastName);

객체 디스트럭처링도 마찬가지로 할당 연산자 왼쪽에 객체 형태의 변수 리스트가 필요하다. 또한 객체 디스트럭처링은 객체에서 프로퍼티 이으로 필요한 프로퍼티 값만을 추출할 수 있다.

이는 예제를 통해 살펴보도록 하자.

Spread 연산자

Spread 연산자는 연산자의 대상 배열이나 반복 가능한 객체를 개별 요소로 분리한다. Rest 파라미터와는 다르다는 것을 주의하여야 한다.

  • 함수를 인자로 사용하는 경우
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// ES5
function foo(x,y,z) {
console.log(x);
console.log(y);
console.log(z);
}

const arr = [1,2,3];

foo.apply(null, arr);

// ES6
function foo(x,y,z) {
console.log(x);
console.log(y);
console.log(z);
}

const arr = [1,2,3];

foo(...arr);