본 문서는 2022년 2월 2일 에 작성되었습니다.
강력한 Operator 인 ... 을 기반으로 작동하는
Spread, Rest Synytax (전개, 나머지 구문) 을 알아보겠습니다.
... 의 정식 명칭은 Spread Operator 입니다.
... Operator 는 구조를 파괴한다는 점에서 Destructuring 과 유사합니다.
구조 분해 할당 구문은 배열이나 객체의 속성을 해체하여 그 값을 개별 변수에 담을 수 있게 하는 JavaScript 표현식입니다. Destructuring mdn
그렇지만 ... Operator 는 다음과 같은 기능을 담당해줍니다.
전개 구문을 사용하면 배열이나 문자열과 같이 반복 가능한 문자를 0개 이상의 인수 (함수로 호출할 경우) 또는 요소 (배열 리터럴의 경우)로 확장하여, 0개 이상의 키-값의 쌍으로 객체로 확장시킬 수 있습니다. ... Operator
위 내용에서 1,2 번을 ✅ Spread Syntax 에서
위 내용에서 3번을 ✅ Rest Syntax 에서 설명하도록 하겠습니다.
전술한 바와 같이 Spread Operator 는
반복 가능한 속성을 가진 배열 등을 분해하는 기능 을 가지고 있습니다.
Spread Syntax, 전개 구문은 이러한 분해의 특성을 이용한 배열 및 객체의 가공 을 의미합니다.
따라서,
과거에는 다양한 클래스의 내장 함수로 구현했어야 했을 문제들을 Spread Syntax 로 해결할 수 있게 되었습니다.
매개변수에 여러 변수를 배열로 넘겨주기 위해서는
과거에는 Array.prototype.apply() 를 사용했어야 했지만,
이제는 Spread Syntax 를 이용하여 복수의 변수를 전달할 수 있습니다.
매개변수를 주고 받을 때,
Spread, Rest Syntax 를 사용하며,
두 구문의 구체적인 차이는 다음과 같습니다.1. Spread Syntax / 매개변수를 넘겨주기 위한...
2. Rest Syntax / 매개변수를 넘겨받기 위한...정확한 차이는 ✅ Rest Syntax 를 확인해주세요.
// Before Spread Syntax
function argsFunc(x,y,z) {}
var array=[0,1,2];
argsFunc.apply(null,args);
// After Spread Syntax
function argsFunc(x,y,z) {}
let array=[0,1,2];
argsFunc(...args);
과거에는 다음과 같은 방법으로 배열을 복사할 수 있었습니다.
또는 push(), splice(), concat() 등의 함수를 사용했어야 했습니다.
아니면 반복문을 통해서 함수를 더 지저분하게 쓰거나...
하지만 Array.from() 과 같이 Shallow Copy 하는 것이 가능해졌습니다.
let arr1=[10,20,30];
let arr2=[...arr1];
단, 다차원 배열일 경우 배열 안의 배열의 주소를 재사용하는 상황이 벌어집니다.
다음의 예시를 보면,
arr1.push(40) 은 기댓값대로 arr1 의 길이만 늘어나지만,
arr1[1].push(40) 은 기댓값과 달리 arr1, arr2 의 1번 배열이 모두 영향을 받습니다.
let arr1=[[10,20,30],[10,20,30],[10,20,30]];
let arr2=[...arr1];
arr1[1].push(40);
console.log(arr1[1]); // 10, 20, 30, 40
console.log(arr2[1]); // 10, 20, 30, 40
arr1.push(40);
console.log(arr1); // Array(3), Array(4), Array(3), 40
console.log(arr2); // Array(3), Array(4), Array(3)
이러한 Deep in Shallow Copy 는 Array.from() 으로 복사를 해도 동일하게 발생을 합니다. 따라서, 다음과 Array Destructuring 을 통해서 문제를 해결할 수 있습니다.
하지만 arr1[0],[1],[2] 는 a,b,c 와 연결되어 있음을 잊지 말아야 합니다.
const arr1=[[10,20,30], [10,20,30], [10,20,30]];
const [a,b,c]=arr1;
const arr2[[...a],[...b],[...c]];
여러 배열 혹은 하나의 배열과 값을 연결하기 위해서
과거에는 Array.prototype.concat() 을 사용했어야 했지만,
이제는 Spread Syntax 를 이용하여 손쉽게 연결할 수 있습니다.
// Before Spread Syntax
const arr1=[10,20,30];
const arr2=[40,50,60];
const arr3=arr1.concat(arr2);
const arr4=arr1.concat([40]);
const arr5=arr1.concat(arr2).concat([70]);
// After Spread Syntax
const arr1=[10,20,30];
const arr2=[40,50,60];
const arr3=[...arr1,...arr2];
const arr4=[...arr1,40];
const arr5=[...arr1,...arr2,70];
객체를 복사하기 위해서 Object > Object.assign() 를 사용했어야 했지만,
이제는 Spread Syntax 를 이용하여 객체를 복사할 수 있습니다.
기본적으로 Shallow Copy 이며,
Object.assign() 은 setter(설정자) mdn 까지 복사하지만,
Spread Syntax 는 이를 복사하지 못한다.
아래의 예시들을 확인해보자
const user={
name:"test1",
age:26
};
const user2={
name:"test2",
height:200
};
const user3={ ...user1, ...user2 };
// name "test2"
// age 26
// height 200
function returnUser(name) {
return ({ name });
}
function merge(...users) {
return { ...users }
}
const user1=returnUser("unchaptered");
const user2=returnUser("hallow");
const userList=merge(user1,user2);
// 0:user1
// 1:user2
function returnUser(name) {
return ({ name });
}
function merge(...users) {
return [ users ]
}
const user1=returnUser("unchaptered");
const user2=returnUser("hallow");
const userList=merge(user1,user2);
const user1 = {"name": "unchaptered"};
const array = [...obj]; // TypeError: obj is not iterable
Destructuring > Object.Destructuring > 유효하지 않은 식별자명 에서 배운 기능을 활용해서 이를 Spread Syntax 에 적용할 수 있다.
const {"name":name} = {"name": "unchaptered"};
const array = [...obj];
// name unchaptered
전술한 바와 같이 Spread Opertaor 는
반복 가능한 속성을 가진 배열 등을 분해하는 기능 을 가지고 있습니다.
Rest Syntax, 나머지 구문은 이러한 분해의 특성을 이용한 나머지 매개변수 설정 을 의미합니다.
함수에서 매개변수의 수를 확정할 수 없을 수 있습니다.
과거에는 arguments 객체를 사용했어야 했지만,
이제는 Rest Syntax 를 이용하여 복수의 변수를 받도록 지시할 수 있습니다.
// Before Rest Syntax
function testArguments(title, numArguments) {
console.log(arguments[0]);
console.log(arguments[1]);
console.log(arguments[2]);
console.log(arguments[3]);
arguments.forEach((value,key)=>console.log(key+1, value));
}
testArguments("unchaptered","one","two","three");
// unchaptered
// one
// two
// three
// Uncaught TypeError: arguments.forEach is not a function
// After Rest Sytntax
function testRest(title, ...numList) {
console.log(title);
console.log(numList);
numList.forEach((value,key)=>console.log(key+1, value));
}
testRest("unchaptered","one","two","three");
// unchaptered
// Array(3) [ "one", "two", "three" ];
// 1, one
// 2, two
// 3, three
- arguments 객체는 실제 배열이 아니다.
- arguments 객체는 callee() 와 같은 추가 함수를 포함합니다.
- Rest Syntax 는 후속 매개변수만 포함하지만, arguments 는 전후의 매개변수를 모두 포함합니다.
Map.set() 이나 Object setter 처럼
객체에 특정 값을 새로 넣는 메서드를 만드는데 Rest Syntax 를 사용할 수 있습니다.
다음의 setter,설정자 와 Rest Syntax, 나머지 구문 의 예제 준비해봤습니다.
- Using setter to add Value
- Using setter to add Value with Destructuring
- Using Rest Syntax to add Default Value
- Using Rest Syntax to add Custom Value or Default Value
// Using setter to add Value
const user={
username:"unchaptered",
userpw: "password",
set setLocation(location) {
this.location=location;
}
}
user.setLocation="Korea";
// Using setter to add Value with Destructuring
const user={
username:"unchaptered",
userpw: "password",
set setLocation(location="Korea") {
this.location=location;
}
}
user.setLocation=undefined;
// Using Rest Syntax to add Default Value
const user={
username:"unchaptered",
userpw:"password",
};
function addElement({ location="Korea", ...others}) {
return ({ location, ...others });
};
const newUser=addElement(user);
// Using Rest Syntax to add Custom Value or Default Value
const user={
username:"unchaptered",
userpw:"password",
};
function addElement(user, location="Korea") {
return ({...user, location});
};
const newUser1=addElement(user);
const newUser2=addElement(user,"American");
# 요소 추가, 어떤 기준으로 선택해야 할까?
두 방식 모두 없던 요소를 추가하는 점 은 동일합니다.
또한 두 방식 모두 이미 있는 요소를 덮어씌울 수 있는 점도 동일합니다.따라서 객체의 특성이 코드 전반에서 사용된다면,
OOP 의 이념을 따라서 setter() 를 설정하는 것이단순하게 임의 요소를 추가하는 일회용 작용이라면,
코드의 효율성을 위해서 Rest Syntax 를 사용하는 것이 어떤가 생각해봅니다.
Destructuring > Object.Destructuring 을 이용하면
객체의 요소 이름을 변경할 수 있습니다.
하지만 한개 혹은 소수의 요소 명을 변경하기 위해서,
다음과 같이 Rest Syntax 를 활용할 수 있습니다.
해당 구문의 강점은 객체가 많은 요소를 가지고 있고 그 중 일부만 이름을 수정 해야하는 순간에 강하며 다음의 3번 코드와 같습니다.
- Using Object Destructuring Without Rest Syntax 1
- Using Object Destructuring Without Rest Syntax 2
- Using Object Destructuring With Rest Syntax
// Using Object Destructuring Without Rest Syntax - 1
let user={
username:"unchaptered",
userpw:"password",
};
function renameELement(user) {
return ({ name:user.username, pw:user.userpw });
}
user=renameELement(user);
// Using Object Destructuring Without Rest Syntax - 2
let user={
username:"unchaptered",
userpw:"password",
};
function renameELement({username:name, userpw:pw) {
return ({ name, pw });
}
user=renameELement(user);
// Using Object Destructuring With Rest Syntax
let user={
username:"unchaptered",
userpw:"password",
email: "email",
test1: "test1",
test2: "test2",
test3: "test3",
test4: "test4",
};
function renameELement({username:name,userpw:pw, ...others}) {
return ({name,pw,...others});
}
user=renameELement(user);
Destructuring > Object.Destructuring 을 이용하면
객체의 요소를 제거할 수 있습니다.
하지만 한개 혹은 소수의 요소를 제거하는데,
다음과 같이 Rest Syntax 를 활용할 수도 있습니다.
해당 구문의 강점은 객체가 많은 요소를 가지고 있고 그 중 일부만 제거해야하는 순간에 강하며 다음의 3번 코드와 같습니다.
- Using Object Destructuring Without Rest Syntax 1
- Using Object Destructuring Without Rest Syntax 2
- Using Object Destructuring With Rest Syntax
// Using Object Destructuring Without Rest Syntax - 1
let user={
username:"unchaptered",
userpw:"password",
};
function removePassword(user) {
return ({ username:user.username });
}
user=removePassword(user);
// Using Object Destructuring Without Rest Syntax - 2
let user={
username:"unchaptered",
userpw:"password",
};
function removePassword({username:user}) {
return ({ user });
}
user=removePassword(user);
// Using Object Destructuring With Rest Syntax
let user={
username:"unchaptered",
userpw:"password",
email: "email",
test1: "test1",
test2: "test2",
test3: "test3",
test4: "test4",
};
function renameELement({userpw, ...others}) {
return ({...others});
}
user=renameELement(user);