es6
Rest parameters vs. Spread operator


Rest parameters
Math.max(arg1, arg2, ..., argN) => returns the greatest of the arguments. Object.assign(dest, src1, ..., srcN) => copies properties from src1..N into dest.
- 첫 번째 argument(dest)에 두번째부터 끝까지의 argument들을 통합해서 첫 번째 argument(dest)에다가 property들을 다 copy한다. ⇒ Object.assign
…and so on.
- Math.max(arg1, arg2, ..., argN) 나 Object.assign(dest, src1, ..., srcN) 처럼 중간에 ... 에 하나가 들어갈지 두개가 들어갈지 여러개가 들어갈지... 즉 몇개가 들어갈 지 모르는 경우 쓰는 게 Rest parameters 이다. ⇒ 즉, argument 숫자가 확정되지 않았을 때 쓸 수 있다.
다음 예시를 보자.
function sum(a, b) { return a + b; } console.log(sum(1, 2, 3, 4, 5));
위와 같은 함수가 있을 때, 주어진 인자는 1, 2, 3, 4, 5 이지만, 쓰여지는 것은 1, 2 뿐이다. 하지만 따로 에러가 나오지 않고 쓰이지 않는 숫자는 undefined로 처리해주고 쓰여지는 숫자로만 리턴값을 구성하는 것이 Javascript의 특이한 방식이다.
위의 함수를 다음과 같이 리팩토링 해보자.
function sumAll(...args) { // args is the name for the array let sum = 0; for (let arg of args) sum += arg; return sum; } console.log(sumAll(1)); // 1 console.log(sumAll(1, 2)); // 3 console.log(sumAll(1, 2, 3)); // 6
- "..." 을 쓰는 것, 즉 rest parameters를 이용한다는 것은 argument의 개수가 몇 개이든 상관없다는 것이다. 즉, argument가 몇개가 오든 우리가 원하는 결과값을 도출 가능한 것이다.
다른 예시를 보면서 rest parameter에 대해 정확히 이해해보도록 하자.
function showName(firstName, lastName, ...titles) { // ...titles is an array. console.log(firstName + ' ' + lastName); // Julius Caesar // the rest go into titles array // i.e. titles = ["Consul", "Imperator"] console.log(titles[0]); // Consul console.log(titles[1]); // Imperator console.log(titles.length); // 2 } showName("Julius", "Caesar", "Consul", "Imperator");
함수 자체의 arguments 를 콘솔에 출력해보면 rest parameter를 이해해보자.
function showName() { // arguments is a keyword of JS. and similar to array, but not. . console.log( arguments.length ); console.log( arguments[0] ); console.log( arguments[1] ); // it's iterable // for(let arg of arguments) alert(arg); } // shows: 2, Julius, Caesar showName("Julius", "Caesar"); // shows: 1, Ilya, undefined (no second argument) showName("Ilya");function f() { let showArg = () => console.log(arguments[0]); // Arrow function showArg(); } f(1); // 1 f(2, 3); // 2 // 참고 // "this" keyword of an arrow function denotes its surrounding context(= execution stack). // Arrow function의 특징 // Arrow function의 this라고 하는 keyword는 자기가 아니라 자기를 둘러싸고 있는 context를 가리킨다.
Spread operator
다음 코드를 보면서 spread operator의 역할과 기능을 이해해보자.
let arr = [3, 5, 1]; console.log(Math.max(arr)); // null let arr = [3, 5, 1]; console.log(Math.max(3, 5, 1)); // 5 // 맨 위의 식을 Spread operator을 이용하면 우리가 원하는 두번째 식의 결과값을 도출할 수 있다. let arr = [3, 5, 1]; console.log(Math.max(...arr)); // 5
두 번쩨 예시도 보도록 하자.
let arr1 = [1, -2, 3, 4]; let arr2 = [8, 3, -8, 1]; console.log(Math.max(...arr1, ...arr2)); // 8 /// 위 식의 ...은 Spread이다. rest는 하나인 경우에만 쓸 수 있지 두개를 쓸 수 없다. let merged = [0, ...arr1, 2, ...arr2]; console.log(merged) // [0, 1, -2, 3, 4, 2, 8, 3, -8, 1] // spread는 rest와는 다르게 parameter로만 쓰이는 것이 아니다. => spread, not rest. let merged = Math.max(0, ...arr1, 2, ...arr2); console.log(merged) // 8 // parameter라고 하더라도 이건 rest가 아니라 spread인 이유는 일단 rest 같은 경우는 맨 뒤에서만 나올 수 있다. // 둘째로 rest는 하나인 경우에만 쓸 수 있지 두개를 쓸 수 없디. => spread, not rest.
마지막 예시를 보도록 하자.
let str = "Hello"; // string is an array of characters. console.log([...str]); // ["H", "e", "l", "l", "o"] console.log(Array.from(str)); // ["H", "e", "l", "l", "o"] // 위 두 식은 같은 결과값을 반환한다.
Rest parameters vs. Spread operator
둘의 모양은 같지만, 하는 역할이 다르다.
그 '다름'은 문맥, 상황을 통해서만 파악할 수 있다.
Spread는 Rest와는 다르게 parameter로만 쓰이는 것이 아니다.
Rest는 하나만 쓸 수 있지 두개를 쓸 수 없고 인자로 담길 때 맨 뒤에서만 사용할 수 있다.
Rest parameters 예제 추가
function adder(...a) { console.log(a); // [1, 4, 6, 7, 3, 234] return a.reduce((sum, number) => { return sum + number; }, 0) } const ab = adder(1, 4, 6, 7, 3, 234); console.log(ab); // 255 --- const MathLibrary = { multilpyTwoNumbers(a, b) { return a * b; }, multiply(...rest) { return rest.reduce((total, elem) => total * elem, 1); }, }; const a = MathLibrary.multilpyTwoNumbers(4, 5); console.log(a); // 20 const b = MathLibrary.multiply(4, 5, 6); console.log(a); // 120 // multilpyTwoNumbers는 multiply 메소드가 생겼기 때문에 불필요해진다. // 하지만 이미 이를 사용하고 있던 수많은 코드가 있다면, // 우린 다음과 같이 리팩토링을 해줄 수 있다. multilpyTwoNumbers(a, b) { return this.multiply(a, b); } // 혹은 multilpyTwoNumbers(...rest) { return this.multiply(...rest); } // 위와 같이 리팩토링 해주면 이미 multilpyTwoNumbers함수를 쓰고 있는 다른 코드들은 아무런 영향을 받지 않고 정상적으로 동작할 수 있다.
Spread operator 예제 추가
const oldHeroes = ['Superman', 'Batman', 'Spiderman', 'Hulk']; const newHeroes = ['Itonman', 'Antman', 'Storm', 'X-man']; const totalHeroes = oldHeroes.concat(newHeores); console.log(totalHeroes); // ['Superman', 'Batman', 'Spiderman', 'Hulk', 'Itonman', 'Antman', 'Storm', 'X-man']; // es6 - spread operator const newTotal = [...oldHeroes, ...newHeores]; console.log(newTotal); // ['Superman', 'Batman', 'Spiderman', 'Hulk', 'Itonman', 'Antman', 'Storm', 'X-man']; const totalHeroesPlusAquaman = ['Aquaman', ...oldHeroes, ...newHeores]; console.log(totalHeroesPlusAquaman); // ['Aquaman', 'Superman', 'Batman', 'Spiderman', 'Hulk', 'Itonman', 'Antman', 'Storm', 'X-man']; function validate(arg) { if (totalHeroes.indexOf(arg) < 0){ return [arg, ...totalHeores]; } } const a = validate('Atom'); console.log(a); // ['Atom', 'Superman', 'Batman', 'Spiderman', 'Hulk', 'Itonman', 'Antman', 'Storm', 'X-man'];
Rest parameters vs. Spread operator 연습 문제
문제
function product(a, b, c, d, e) { let numbers = [a, b, c, d, e]; return numbers.reduce(function (acc, number) { return acc * number; }, 1) }; function unshift(array, a, b, c, d, e) { return [a, b, c, d, e].concat(array); }; function join(array1, array2) { return array1.concat(array2); };
위의 코드를 Rest parameters와 Spread operator를 사용하여 간결하게 바꾸어 보도록 하자.
소스 코드
function product(...rest) { return rest.reduce((acc, number) => acc * number, 1) }; function unshift(array, ...rest) { return [rest, ...array]; }; function join(array1, array2) { return [...array1, ...array2]; };