자바스크립트/자바스크립트 정리

[자바스크립트] 스프레드 문법

스프레드 문법

JS

ES6 에서 스프레드 문법이 도입되었습니다. 스프레드 문법은 뭉쳐 있는 여러 값을 펼쳐서(spread) 개별적인 값의 목록을 만들어 냅니다. 

 

주의 할 점은 스프레드 문법은 for...of 문으로 순회 가능한 이터러블에 한정됩니다. 또한 스프레드는 개별적인 값을 만드는 것이 아닌 값들의 목록을 만드는 것이기 때문에 이를 값을 생성하는 연산자로 생각하면 안 됩니다. 즉, 스프레드 문법의 결과를 변수에 할당이 불가능합니다. (변수에는 값만 할당이 가능합니다.)

 

// ...[1, 2, 3]은 [1, 2, 3]을 개별 요소로 분리한다(→ 1, 2, 3)
console.log(...[1, 2, 3]); // 1 2 3

// 이터러블이 아닌 일반 객체는 스프레드 문법의 대상이 될 수 없다.
console.log(...{ a: 1, b: 2 });
// TypeError: Found non-callable @@iterator

// 스프레드 문법의 결과는 값이 아니다.
const list = ...[1, 2, 3]; // SyntaxError: Unexpected token ...

스프레드를 함수 호출문의 인수 목록으로 사용하기

배열을 펼쳐서 함수의 인수 목록으로 전달해야 하는 경우가 종종 있습니다. 예를 들면 Math.max 메서드의 경우 가변 인자 함수로 정해져 있지 않은 여러 숫자를 인수로 받아서 그 중의 최대값을 반환합니다. 이 경우 배열을 인수로 전달하면 제대로 동작할 것 같지만 NaN 을 반환합니다.

const arr = [1, 2, 3];

// 배열 arr의 요소 중에서 최대값을 구하기 위해 Math.max를 사용한다.
const max = Math.max(arr); // -> NaN

 

이때 스프레드 문법을 사용하면 간결하고 가독성 좋게 해결할 수 있습니다.

const arr = [1, 2, 3];

// 스프레드 문법을 사용하여 배열 arr을 1, 2, 3으로 펼쳐서 Math.max에 전달한다.
// Math.max(...[1, 2, 3])은 Math.max(1, 2, 3)과 같다.
const max = Math.max(...arr); // -> 3

 

사용시 주의해야 할 점은 스프레드 문법은 Rest 파라미터와 형태가 동일해서 혼동할 수 있다는 점입니다. Rest 파라미터는 매개변수의 이름 앞에 ...을 붙여 함수에 전달된 인수들의 목록을 배열로 받습니다. 스프레드 문법과는 서로 반대의 개념이지만 형태가 동일하므로 주의해야 합니다.

// Rest 파라미터는 인수들의 목록을 배열로 전달받는다.
function foo(...rest) {
  console.log(rest); // 1, 2, 3 -> [ 1, 2, 3 ]
}

// 스프레드 문법은 배열과 같은 이터러블을 펼쳐서 개별적인 값들의 목록을 만든다.
// [1, 2, 3] -> 1, 2, 3
foo(...[1, 2, 3]);

배열 리터럴 내부에서 사용하기

배열 리터럴에서 스프레드 문법을 사용하면 ES5 보다 더 간결하고 가독성 좋게 코드를 표현하는 것이 가능합니다. ES5 의 방식과 비교하면서 살펴보도록 하겠습니다.

 

1. concat

ES5 에서 2개의 배열을 1개의 배열로 결합하고 싶은 경우에는 concat 을 사용했었습니다. ES6 에서 스프레드 문법을 사용하면 별도의 메서드를 사용하지 않고도 배열 리터럴만으로 1개의 배열로 합치는 것이 가능합니다.

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

// ES6
const arr = [...[1, 2], ...[3, 4]];
console.log(arr); // [1, 2, 3, 4]

2. splice

ES5 에서 배열의 중간에 배열 요소를 추가하거나 제거하려면 splice 메서드를 사용합니다. 이 때 세 번째 인수로 배열을 전달하면 배열 자체가 추가됩니다.

// ES5
var arr1 = [1, 4];
var arr2 = [2, 3];

// 세 번째 인수 arr2를 해체하여 전달해야 한다.
// 그렇지 않으면 arr1에 arr2 배열 자체가 추가된다.
arr1.splice(1, 0, arr2);

// 기대한 결과는 [1, [2, 3], 4]가 아니라 [1, 2, 3, 4]다.
console.log(arr1); // [1, [2, 3], 4]

 

스프레드 문법을 사용하면 이러한 문제를 해결하는 동시에 간결하고 가독성 좋게 표현이 가능합니다.

// ES6
const arr1 = [1, 4];
const arr2 = [2, 3];

arr1.splice(1, 0, ...arr2);
console.log(arr1); // [1, 2, 3, 4]

3. 배열 복사

ES5 에서 slice 를 사용해 배열을 복사했었지만 스프레드 문법을 통해 간결하고 가독성 좋게 표현이 가능합니다. 이때 slice 와 마찬가지로 얕은 복사로 새로운 복사본을 생성합니다.

// ES5
var origin = [1, 2];
var copy = origin.slice();

console.log(copy); // [1, 2]
console.log(copy === origin); // false

// ES6
const origin = [1, 2];
const copy = [...origin];

console.log(copy); // [1, 2]
console.log(copy === origin); // false

이터러블을 배열로 변환하기

ES5 에서 이터러블을 배열로 변환하려면 Function.prototype.apply 나 Function.prototype.call 메서드를 사용하고 slice 메서드를 사용해야 했습니다. 이 방법을 통해 이터러블인 유사 배열 객체도 배열로 변환이 가능했습니다.

// ES5
function sum() {
  // 이터러블이면서 유사 배열 객체인 arguments를 배열로 변환
  var args = Array.prototype.slice.call(arguments);

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

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

// 이터러블이 아닌 유사 배열 객체
const arrayLike = {
  0: 1,
  1: 2,
  2: 3,
  length: 3
};

const arr = Array.prototype.slice.call(arrayLike); // -> [1, 2, 3]
console.log(Array.isArray(arr)); // true

 

스프레드 문법을 사용하면 이터러블을 배열로 변경하는 것이 가능합니다. 하지만 이터러블하지 않다면 스프레드 문법의 적용과 사용이 불가능합니다. 이터러블이 아닌 유사 배열 객체를 배열로 변경하고 싶다면 Array.from 메서드를 사용하는 것이 좋습니다.

// ES6
function sum() {
  // 이터러블이면서 유사 배열 객체인 arguments를 배열로 변환
  return [...arguments].reduce((pre, cur) => pre + cur, 0);
}

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

// 이터러블이 아닌 유사 배열 객체
const arrayLike = {
  0: 1,
  1: 2,
  2: 3,
  length: 3
};

const arr = [...arrayLike];
// TypeError: object is not iterable (cannot read property Symbol(Symbol.iterator))

// Array.from은 유사 배열 객체 또는 이터러블을 배열로 변환한다
Array.from(arrayLike); // -> [1, 2, 3]

객체 리터럴 내부에서 사용하기

현재 TC39 프로세스의 stage 4(Finished) 단계에 제안되어 있는 스프레드 프로퍼티 사용을 통해 객체 리터럴의 프로퍼티 목록에서도 스프레드 문법을 사용할 수 있지만. 원래 스프레드 문법의 대상은 이터러블이어야 하지만 스프레드 프로퍼티 제안은 일반 객체를 대상으로도 스프레드 사용을 허용합니다.

// 스프레드 프로퍼티
// 객체 복사(얕은 복사)
const obj = { x: 1, y: 2 };
const copy = { ...obj };
console.log(copy); // { x: 1, y: 2 }
console.log(obj === copy); // false

// 객체 병합
const merged = { x: 1, y: 2, ...{ a: 3, b: 4 } };
console.log(merged); // { x: 1, y: 2, a: 3, b: 4 }

마무리

ES6 에 도입된 스프레드 문법을 사용하면 간결하고 가독성 좋은 코드를 짜는 것이 가능합니다. 많이 사용되는 문법이므로 자세히 알아두는 것이 좋다고 생각합니다. 앞서 배운 이터러블한 경우에만 사용하는 것이 가능하니, 이 점 주의해서 사용하면 좋을 것 같습니다.

 

보다 더 자세한 내용은 아래 링크에서 확인 가능하고, 저 또한 내용들을 공부하며 정리하기 위한 목적으로 글을 남기는 것을 알려드립니다. 읽어주셔서 감사합니다!

 

출처 : https://poiemaweb.com/