ES6에서 도입된 스프레드 문법(spread syntax) ... 는 하나로 뭉쳐 있는 여러 값들의 집합을 펼쳐서 개별적인 값들의 목록으로 만든다. 스프레드 문법을 사용할 수 있는 대상은 순회할 수 있는 이터러블에 한정된다.
const list = ... [1, 2, 3]; // ❌ SyntaxError
❗ 스프레드 문법은 값을 생성하는 연산자가 아니다. 따라서 스프레드 문법의 결과는 변수에 할당할 수 없다.
const arr = [1, 2, 3];
Math.max(...arr); // 3
Math.max(arr); // NaN
❓ Rest 파라미터 vs 스프레드 문법
👉 Rest 파라미터는 함수에 전달된 인수들의 목록을 배열로 전달받기 위해 매개변수 이름 앞에 ...을 붙이는 것이다. 스프레드 문법은 여러 개의 값이 하나로 뭉쳐 있는 배열과 같은 이터러블을 펼처서 개별적인 값들의 목록을 만드는 것이다. 따라서 Rest 파라미터와 스프레드 문법은 서로 반대의 개념이다.
// 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개의 배열을 1개의 배열로 결합할 수 있다.
const arr1 = [1, 4];
const arr2 = [2, 3];
// arr1.splice(1, 0, arr2); // ES5 [1, [2, 3], 4]
arr1.splice(1, 0, ...arr2); // ES6
console.log(arr1); // [1, 2, 3, 4]
// ES6
const origin = [1, 2];
const copy = [...origin];
console.log(copy); // [1, 2]
console.log(copy === origin); // false
✔ 원본 배열의 각 요소를 얕은 복사하여 새로운 복사본을 생성한다.
const sum = (...args) => args.reduce((pre, cur) => pre + cur, 0);
console.log(sum(1, 2, 3,)); // 6
✔ Rest 파라미터 args는 함수에 전달된 인수들의 목록을 배열로 전달받는다.
❗ 이터러블이 아닌 유사 배열 객체는 스프레드 문법의 대상이 될 수 없다.
✨ ES6에서 도입된 Array.from 메서드를 사용하여 이터러블이 아닌 유사 배열 객체를 배열로 변경 할 수 있다.
stage 4 단계에 제안되어 있는 스프레드 프로퍼티를 사용하면 객체 리터럴의 프로퍼티 목록에서도 스프레드 문법을 사용할 수 있다. 스프레드 프로퍼티는 Object.assign 메서드를 대체할 수 있는 간편한 문법이다.
// 객체 병합. 프로퍼티가 중복되는 경우 뒤에 위치한 프로퍼티가 우선권을 갖는다.
const merged = { ... { x:1, y:2 }, ... { y:10, z:3} };
console.log(merged); // { x:1, y:10, z:3 }
// 특정 프로퍼티 변경
const changed = { ... {x: 1, y: 2 }, y: 100 };
console.log(changed); // { x:1, y:100 }
// 프로퍼티 추가
const added = { ... {x:1, y:2}, z: 0};
console.log(added) // { x:1, y:2, z:0 }
구조 분해 할당(destructuring assignment)은 구조화된 배열과 같은 이터러블 또는 객체를 destructuring(비구조화, 구조 파괴) 하여 1개 이상의 변수에 개별적으로 할당하는 것을 말한다.
ES6 배열 디스트럭처링 할당은 배열의 각 요소를 배열로 부터 추출하여 1개 이상의 변수에 할당하는데, 배열 디스트럭처링 할당의 대상(할당문의 우변)은 이터러블이어야 하며, 할당 기준은 배열의 인덱스이다.
const arr = [1, 2, 3];
const [one, two, three] = arr;
console.log(one, two, three); // 1 2 3
const [four, five] = {}; // ❌ TypeError
✔ 배열 구조분해 할당을 위한 변수에 기본값을 설정할 수 있다. 단, 기본값보다 할당된 값이 우선한다.
const [e, f = 10, g = 3] = [1, 2];
console.log(e, f, g); // 1 2 3
✔ 배열 구조분해 할당을 위한 변수에 Rest 파라미터와 유사하게 Rest 요소 ...를 사용할 수 있다.
const [x, ...y] = [1, 2, 3];
console.log(x, y); // 1 [ 2, 3]
ES6의 객체 구조분해 할당은 객체의 각 프로퍼티를 객체로부터 추출하여 1개 이상의 변수에 할당한다. 이때 객체 구조분해 할당의 대상은 객체이어야 하며, 할당 기준은 프로퍼티 키다. 즉, 순서는 의미가 없으며 선언된 변수 이름과 프로퍼티 키가 일치하면 할당된다.
const user = { firstName: 'Yongwoo', lastName: 'Cho' };
const { lastName, firstName } = user;
console.log(firstName, lastName); // Yongwoo Cho
✔ 할당 연산자 왼쪽에 프로퍼티 값을 할당받을 변수를 선언해야 하는데 이때 변수를 객체 리터럴로 선언한다.
❗ 우변에 객체 또는 객체로 평가될 수 있는 표현식(문자열, 숫자, 배열 등)을 할당하지 않으면 에러가 발생한다.
✔ 객체 구조분해 할당을 위한 변수에 기본값을 설정할 수 있다.
const { firstName = 'Yongwoo', lastName } = { lastName: 'Cho' };
console.log(firstName, lastName); // Cho Yongwoo
✔ 객체 구조분해 할당은 객체에서 프로퍼티 키로 필요한 프로퍼티 값만을 추출하여 변수에 할당하고 싶을 때 유용하다.
const todo = { id: 1, content: 'HTML', completed: true};
// todo 객체로부터 id 프로퍼티만 추출한다.
const { id } = todo;
console.log(id); // 1
✔ 객체를 인수로 전달받는 함수의 매개변수에도 사용할 수 있다.
function printTodo(todo) {
console.log(
`할일 ${todo.content}은 ${todo.completed ? '완료' : '비완료'} 상태입니다.`
);
}
// 매개변수 todo에 객체 구조분해 할당을 사용하여 가독성이 좋은 코드
function printTodo({ content, completed }) {
console.log(`할일 ${content}은 ${completed ? '완료' : '비완료'} 상태입니다.`);
}
✔ 배열의 요소가 객체인 경우 배열 구조분해 할당과 객체 구조 분해할당을 혼용할 수 있다.
const todos = [
{ id: 1, content: 'HTML', completed: true },
{ id: 2, content: 'CSS', completed: false },
{ id: 3, content: 'JS', completed: false }
];
const [, { id }] = todos;
console.log(id); // 2
✔ 객체 구조분해할당을 위한 변수에 Rest 프로퍼티 ... 를 사용할 수 있다. Rest 프로퍼티는 Rest 파라미터나 Rest 요소와 마찬가지로 반드시 마지막에 위치해야 한다.
const { x, ...rest } = { x: 1, y: 2, z: 3 };
console.log(x, rest); // 1 {y:2, z:3}