구조 분해 할당에 앞서서, 구조 분해 할당에 많이 응용되는 spread operator를 먼저 알아보자!!
Spread operator는 ES6에서 도입된 이후, 많이 사용되고 있다. 요즘은 곧 사라질 IE 외에는 모든 브라우져가 ES6를 지원하고, 타입스크립트 같이 컴파일하여 사용하는 언어도 많이 사용하므로 큰 부담 없이 많이 사용되는 것 같다.
spread operator를 한 마디로 표현하자면 "배열 요소를 한 번에 펼쳐준다"이다.
하지만, 백문이 불여일견이라고 직접 예문을 보는 게 100번 낳다!!
// arr은 모든 예시에 다 적용된다고 가정하자!!
const arr = ["a1", "a2", "a3"];
// 배열의 형태가 사라진 것을 확인할 수 있다.
console.log(...arr); // => a1 a2 a3
기존에 두 개의 배열을 결합하는 데 있어서 concat 메서드를 이용하곤 했다.
ES6에서는 spread 연산자를 이용하여 좀 더 깔끔한 배열 병합이 가능하다.
// 1. 배열의 추가
const num2 = [10, 20, 30, ...arr];
console.log(num2); // [ 10, 20, 30, "a1", "a2", "a3"]
const num1 = [1, 2, ...arr, 3];
// push() 사용하지 않고도, 이렇게 간단하게 배열 어디에나 집어넣을 수 있다.
console.log(num1); // [ 1, 2, "a1", "a2", "a3", 3 ]
- arr이 변경된다고 해서, num3 값이 변경되지는 않는다. 즉, 새로운 복사된 배열을 생성할 수 있다.
const num3 = [...arr];
console.log(num3); // ["a1", "a2", "a3"]
arr === num3; // false
const arr1 = arr;
arr1 === arr; // true
const num4 = [...arr, ...num1, ...num2];
console.log(num4);
// ["a1", "a2", "a3", 1, 2, "a1", "a2", "a3", 3, 10, 20, 30, "a1", "a2", "a3"]
let arr1 = [{name: '조은길', age: 20}];
let arr2 = [...arr1];
arr2[0].name = '변경된 이름';
// arr2 의 변화가 arr1에도 똑같이 미친다.
console.log(arr1); // [ { name: '변경된 이름', age: 20 } ]
console.log(arr2); // [ { name: '변경된 이름', age: 20 } ]
let arr3 = [['조은길', 20]];
let arr4 = [...arr3];
arr4[0][0] = '변경된 이름';
console.log(arr3); // [ [ '변경된 이름', 20 ] ]
console.log(arr4); // [ [ '변경된 이름', 20 ] ]
Rest Parameter
함수를 호출할 때 함수의 매개변수(parameter)를 spread operator로 작성한 형태를 Rest parameter라고 부른다. Rest 파라미터를 사용하면 함수의 파라미터로 오는 값들을 모아서 "배열"에 집어넣는다. 이를 통해서 깔끔한 함수 표현을 적용할 수 있다.
// num은 모든 예시에 다 적용된다고 가정하자!!
const num = [1, 2, 3, 4];
function sum(x, y, z) {
return x + y + z;
}
// x === num[0], y === num[1], z === num[2]
console.log(sum(...num)); // 6
// x === 10 , y === num[0] , z === num[1]
console.log(sum(10, ...num)); // 13
// ...rest 는 배열 형태로 전환된다.
function add(...rest) {
let sum = 0;
for (let item of rest) {
sum += item;
}
return sum;
}
console.log( add(1) ); // 1
console.log( add(1, 2) ); // 3
console.log( add(1, 2, 3) ); // 6
ex 1)
function addMul(method, ...rest){
if (method === 'add') {
let sum = 0;
for (let item of rest) {
sum += item;
}
return sum;
} else if (method === 'multiply'){
let mul = 1;
for (let item of rest) {
mul *= item;
}
return mul;
}
}
console.log( addMul('add', 1,2,3,4) ); // 10
console.log( addMul('multiply', 1,2,3,4) ); // 24
ex 2)
// 함수의 매개변수의 적용( 가변적 매개변수 )
function sum2(x, ...arr) {
// arr은 현재 배열의 형태이기 때문에, 다시 한번 ...을 써줘서 배열을 풀어줘야 한다.
return [x, ...arr];
}
console.log(sum2("홍길동", 1)); // ["홍길동", 1 ]
console.log(sum2("홍길동", 1, 2)); // ["홍길동", 1 , 2]
console.log(sum2("홍길동", 1, 2, 3, 4, 5, "hello"));
// ["홍길동", 1, 2, 3, 4, 5, "hello"]
단, Rest파라미터는 항상 제일 마지막 파라미터로 있어야 한다.
function addMul (...rest, method){ }와 같은 방식으로는 사용할 수 없다.
함수 정의 쪽이 아니라 함수를 Call 할 때 spread operator를 사용할 수 있다. 기존에는 배열로 되어있는 내용을 함수의 인자로 넣어주려면 보내주기 전에 Apply나 for문을 통한 추가 작업이 필요했다. 하지만 spread operator를 이용하면 배열 형태에서 바로 함수 인자로 넣어줄 수 있다.
const numbers = [9, 4, 7, 1];
// 이건 진짜 신세게!! ㅇㅈ??
Math.min(...numbers); // 1
Map은 배열을 return하므로, 다양한 응용법이 가능하다.
const input = [{name:'철수',age:12}, {name:'영희',age:12}, {name:'바둑이',age:2}];
//가장 어린 나이 구하기.
const minAge = Math.min( ...input.map((item) => item.age) );
console.log(minAge); // 2
// 함수의 default 매개값
// y=10 : y 값이 전달 되지 않으면, 10으로 할당하고, 전달이 되면 그 값으로 할당한다.
function sum3(x, y = 10, z = 100) {
return x + y + z;
}
console.log(sum3(1)); // 111
console.log(sum3(1, 2)); // 103
console.log(sum3(1, 2, 3)); // 6
console.log(sum3(...[1, 2, 3, 4])); // 6
배열에 대한 Spread Opeator를 지원하는 브라우저들은 객체에 대한 Spread Operator 역시 지원한다.
객체에서 spread operator를 이용하여, 객체의 복사 또는 프로퍼티를 업데이트 할 수 있다.
간단한 State Management 구현을 위해서 다음과 같은 방식으로 응용하여 사용하기도 한다.
조금 헷갈릴 수도 있지만, 익숙해지면 코딩이 편해진다.
간단한 예시)
let currentState = { name: '철수', species: 'human'};
currentState = { ...currentState, age: 10};
console.log(currentState); // {name: "철수", species: "human", age: 10}
// 객체의 프로퍼티를 오버라이드 함으로써 객체가 업데이트되는 것을 응용하기
currentState = { ...currentState, name: '영희', age: 11};
console.log(currentState); // {name: "영희", species: "human", age: 11}
간단한 예시 응용법)
const user = {
name: "김코딩",
company: {
name: "Code States",
department: "Development",
role: {
name: "Software Engineer",
},
},
age: 35,
};
const changedUser = {
...user,
name: "박해커",
age: 20,
};
console.log( changedUser );
/*
{
name: '박해커',
company: {
name: 'Code States',
department: 'Development',
role: { name: 'Software Engineer' }
},
age: 20
}
*/
// 이렇게 하면 user라는 객체 자체가 안으로 들어가 버린다.
// user가 key 값 그리고 객체 그 자체가 value로 들어간다.
// 하지만 ... 스프레드 오퍼레이터를 사용할 경우,
// user의 객체가 풀어져서 객체 안의 내용물들이 그대로 이식 된다.
const overwriteChanges = {
name: "박해커",
age: 20,
...user,
};
console.log( overwriteChanges );
/*
{
name: '김코딩',
age: 35,
company: {
name: 'Code States',
department: 'Development',
role: { name: 'Software Engineer' }
}
}
*/
const changedDepartment = {
...user,
company: {
...user.company,
department: "Marketing",
},
};
console.log( changedDepartment );
/*
{
name: '김코딩',
company: {
name: 'Code States',
department: 'Marketing',
role: { name: 'Software Engineer' }
},
age: 35
}
*/
Spread Operator를 모르고, 요즘 코드 뜯어보면, 100% 이해하기 어렵다.
그만큼, 이제는 모르면 안 되는 구문이고, 동시에 내 것으로 만들면, 코딩이 한결 편해지는 고마운 구문이다.
다음 편은 Spread Operator의 연장선 => 구조 분해 할당입니다.