Spread syntax(...)는 ‘전개구문’이라고 이름 불리운다. 전개 구문을 사용하면 배열이나 문자열과 같이 반복 가능한 문자를 0개 이상의 인수(함수로 호출할 경우) 혹은 요소(배열 리터럴에서 사용할 경우)로 확장하여 사용하거나, 0개 이상의 키-값의 쌍(객체 리터럴에서 사용할 경우)로 확장해 사용할 수 있다.
말이 복잡하지만, 결론적으로 spread syntax는 함수 호출을 하면서 인수에 무언가를 집어넣을 때, 배열 리터럴에 무언가를 집어넣을 때, 객체 리터럴에 무언가를 집어넣을 때 사용한다고 생각하면 된다. 이전에는 apply()를 이용해서 위의 기능을 좀 더 귀찮게 구현했다면, spread syntax를 이용해서 좀 더 깔끔하게 구현할 수 있게 되었다.
// 1) Spread in function calls
myFunction(...iterableObj);
// 2) Spread in array literals
[...iterableObj, '4', 'five', 6];
// 3) Spread in object literals
let objClone = { ...obj};
function sum(x, y, z){
return x+y+z;
}
const numbers = [1, 2, 3];
console.log(sum(...numbers));
//expected output : 6
그리고 sum 함수가 호출될 때 필요한 파라미터의 수보다 numbers에 들어있는 요소의 개수가 더 많아도 상관없다. 어차피 앞에서부터 세번째까지의 요소까지만 받고, 나머지는 무시되기 때문이다.
// 1) Apply for 'new' operator
// spread syntax가 없었으면, constructor에서 사용하기 위해서는 더 복잡한 과정이 필요함
let dateFields = [1970, 0, 1];
let d = new Date(...dateFields);
// 2) push(), splice(), concat()를 사용하지 않고, 전개구문을 사용해서 배열 리터럴에서 활용
var parts = ['shoulders', 'knees'];
var lyrics = ['head', ...parts, 'and', 'toes'];
// lyrics = ['head', 'shoulders', 'knees', 'and', 'toes'];
// 3) copy an array
let arr = [1, 2, 3];
let arr2 = [...arr]; // like arr.slice()
arr.push(4);
// arr2 becomes [1, 2, 3, 4], and arr remains unaffected
// * spread syntax는 one level deep copying이 되기 때문에, copying multidimensional arrays를 하는데는 적합하지 않다.
// 4) a better way to concatenate arrays
// 4-1) concat()를 이용해서 기존에 존재하는 배열에 다른 배열을 합치는 방법
let arr1 = [0, 1, 2];
let arr2 = [3, 4, 5];
// append all items from arr2 onto arr1
arr1 = arr1.concat(arr2);
// 4-2) concat()를 통해서 하는 기능을 더 편하게 구현할 수 있음
let arr1 = [0, 1, 2];
let arr2 = [3, 4, 5];
arr1 = [...arr1, ...arr2];
// arr1 is now [0, 1, 2, 3, 4, 5]
// spread for argument lists, ... can be used anywhere in the array literal, and may be used more than once
// 위에서 보는 거와 같이 spread syntax는 한 배열 내에서 여러 번 사용될 수 있음
// 5) spread in object literals
let obj1 = {foo: 'bar', x: 42};
let obj2 = {foo: 'baz', y: 13};
let clonedObj = {...obj1};
// clonedObj = {foo : 'bar', x: 42}
let mergedObj = {...obj1, ...obj2};
// mergedObj = {foo: 'baz', x: 42, y: 13}
// foo라는 key가 두번 등장하기 때문에 뒤에 오는 value로 덮어씌워지는 것을 볼 수 있음
let obj = {'key1': 'value1'};
let array = [...obj]; // TypeError : obj is not iterable
Rest syntax looks exactly like spread syntax. But, rest syntax is the opposite of spread syntax. Spread syntax “expands” an array into its elements, while rest syntax collects multiple elements and “condenses” them into a single element.
rest parameter syntax는 function에 indefinite number of arguments를 array로써 받아서 실행하는 것을 가능하게 해준다. 한마디로, function에서 몇개의 parameter가 정확하게 들어온다고 정해놓지 않고, 임의의 개수인 arugments들이 들어온다고 가정하고 실행될 수 있게 해준다.
function f(a, b, ...theArgs){
...
}
function myFun(a, b, ...manyMoreArgs){
console.log('a', a)
console.log('b', b)
console.log('manyMoreArgs', manyMoreArgs)
}
myFun('one', 'two', 'three', 'four', 'five', 'six')
// console output:
// a, one
// b, two
// manyMoreArgs, ['three', 'four', 'five', 'six']
위에서 보면, myFun에 6개의 arguments를 대입했는데 a에는 ‘one’이 들어가고, b에는 ‘two’가 들어가고, manyMoreArgs에는 [’three’, ‘four’, ‘five’, ‘six’]가 들어간 배열이 만들어지는 것을 알 수 있다.
만약에 myFun(’one’, ‘two’, ‘three’) 이런식으로 arguments가 들어갔어도, manyMoreArgs는 [’three’]로 생성된다. 즉, 한 개만 들어가도 그 한 개의 요소가 들어가있는 배열이 생성된다.
만약에 myFun(’one’, ‘two’) 이런식으로 두번째까지만 arguments가 들어가도, manyMoreArgs는 []로 생성된다. 즉, 비어있는 배열이 생성된다.
spread syntax는 배열에서 넣을 때 여러 번 사용될 수도 있고, 위치도 상관없지만, rest parameter는 가장 끝에서 한 번만 사용되어야 한다.
// function definition을 할 때, 잘못된 사용 사례1
foo(...one, ...wrong, ...wrong)
// function definition을 할 때, 잘못된 사용 사례2
foo(...wrong, arg2, arg3)
// function definition을 할 때, 올바른 사용 사례
foo(arg1, arg2, ...correct)
rest parameters는, arguments를 normal array로 사용하기 위해 converting하는 것을, 이전보다 훨씬 간편하게 해준다.
// 1) Using rest parameters in combination with ordinary parameters
function multiply(multiplier, ...theArgs){
return theArgs.map(element=>{
return multiplier * element
})
}
let arr = multiply(2, 15, 25, 42)
console.log(arr)
// output : [30, 50, 84]
// 2) Rest parameters are real arrays; the arguments object is not
function sortRestArgs(...theArgs){
let sortedArgs = theArgs.sort()
return sortedArgs
}
console.log(sortRestArgs(5, 3, 7, 1))
// output : 1, 3, 5, 7
// * rest parameters를 쓰지 않고, arguments object를 이용해서 똑같은 기능을 구현하려면,
// arguments object를 먼저 real array로 바꾸는 작업을 해줘야 한다.
function sortArguments(){
let args = Array.from(arguments)
let sortedArgs = args.sort()
return sortedArgs
}
console.log(sortArguments(5, 3, 7, 1))
// output : 1, 3, 5, 7