JavaScript

[JavaScript] #06 - Spread 문법

yoonddo 2023. 1. 10. 13:48

ES6에서 도입 되었으며 뭉쳐있는 여러 값을 펼쳐서 개별적인 값의 목록을 만들어 내는 문법이다.

주의 할 점은 스프레드 문법은 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 파라미터는 매개변수의 이름 앞에 ...를 붙여 함수에 전달된 인수들의 목록을 배열로 받는다.

서로 반대의 개념이지만 형태가 동일하므로 주의해야 한다.


 

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

배열 리터럴에서 스프레드 문법을 사용하면 ES6보다 더 간결하고 가독성 좋게 코드를 표현하는 것이 가능하다.

ES6의 방식과 비교하면서 살펴보도록 하자.

 

1. concat

ES6에서 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]

 

배열 복사

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

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

iterable (이터러블) 이란 반복 가능한 객체를 일반화한 객체를 말한다. 이터러블이란 개념을 사용하면 어떤 객체든for..of 반복문을 적용할 수 있다. 배열은 대표적인 이터러블이며 문자열 역시 이터러블의 예이다.

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 }

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

출처 : https://wonjaetech.tistory.com/entry/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%8A%A4%ED%94%84%EB%A0%88%EB%93%9C-%EB%AC%B8%EB%B2%95