시작으로

ES6에서 ...로 시작하는 형태의 문법이 새롭게 추가되었습니다. ...로 붙어있는 이 표시는 Spread Opertor(전개구문, 스프레드 연산자 등)라 부른다.

그리고 Rest Parameter는 스프레드 연산자를 사용하여 함수의 파라미터를 작성하는 것을 말한다.

Spread OpertorRest Parameter는 무엇이고 가독성을 높여주기 위해 어떻게 사용되는지 알아보도록 하자.


Spread Operator

스프레드 연산자를 사용하면 배열, 문자열, 객체 등 반복 가능한 이터러블 객체들을 개별 요소로 분리하여 사용할 수 있다.

기본 문법

// Array
const arr1 = [1,2];
const arr2 = [3,4,5];
console.log([...arr1, ...arr2]); // (5) [1, 2, 3, 4, 5]
console.log([arr1,arr2]);

// Set
const set1 = new Set([1,2]);
const set2 = new Set([3,4,5]);
console.log([...set1, ...set2]); //(5) [1, 2, 3, 4, 5]

// String
const str1 = 'hello';
console.log(...str1); // h e l l o
console.log([...str1]); // (5) ['h', 'e', 'l', 'l', 'o']

// Object 
const obj1 = {'name':'seongsik', 'old':20};
const obj2 = {'address': 'seoul'};
console.log({...obj1, ...obj2}); // {name: 'seongsik', old: 20, address: 'seoul'}

// Map
const map1 = new Map([['a',1], ['b',2]]);
console.log(...map1); // (2) ['a', 1] (2) ['b', 2]

원본 복사의 문제해결

javascript에서는 새로운 변수에 할당하는 경우 새로운 변수는 기존 변수를 참조하게 된다. 따라서 새로운 변수를 변경하는 경우 원본 변수도 같이 변경된다.

// Array
const arr1 = [1,2,3];
const arr2 = arr1;

arr2.push(4);

console.log(arr1); // (4) [1, 2, 3, 4]
console.log(arr2); // (4) [1, 2, 3, 4]

// Set
const set1 = new Set([1,2]);
const set2 = set1;

set2.add(3);

console.log(set1); // Set(3) {1, 2, 3}
console.log(set2); // Set(3) {1, 2, 3}

// Object
const obj1 = {'name':'seongsik', 'old':20};
const obj2 = obj1

obj2['address'] = 'seoul';
console.log(obj1); // {name: 'seongsik', old: 20, address: 'seoul'}
console.log(obj2); // {name: 'seongsik', old: 20, address: 'seoul'}

// Map
const map1 = new Map([['a',1], ['b',2]]);
const map2 = map1;

map2.set('c', 3);

console.log(map1); // Map(3) {'a' => 1, 'b' => 2, 'c' => 3}
console.log(map2); // Map(3) {'a' => 1, 'b' => 2, 'c' => 3}

스프레드 연산자를 사용하면 새로운 객체를 생성하기 때문에 원본을 참조하지 않는다.

// Array
const arr1 = [1,2,3];
const arr2 = [...arr1];

arr2.push(4);

console.log(arr1); // (3) [1, 2, 3]
console.log(arr2); // (4) [1, 2, 3, 4]

// Set
const set1 = new Set([1,2]);
const set2 = new Set([...set1]);

set2.add(3);

console.log(set1); // Set(2) {1, 2}
console.log(set2); // Set(3) {1, 2, 3}

// Object
const obj1 = {'name':'seongsik', 'old':20};
const obj2 = {...obj1}

obj2['address'] = 'seoul';

console.log(obj1); // {name: 'seongsik', old: 20}
console.log(obj2); // {name: 'seongsik', old: 20, address: 'seoul'}

// Map
const map1 = new Map([['a',1], ['b',2]]);
const map2 = new Map([...map1]);

map2.set('c', 3);

console.log(map1); // Map(2) {'a' => 1, 'b' => 2}
console.log(map2); // Map(3) {'a' => 1, 'b' => 2, 'c' => 3}


Rest Parameter

함수의 매개변수를 spread operator로 작성한 형태를 Rest parameter(나머지 매개변수)라고 부른다.

나머지 매개변수 구문을 사용하면 함수가 정해지지 않은 수의 매개변수를 배열로 받을 수 있다.

즉, 함수의 파라미터가 몇 개가 됐든 모두 모아서 배열로 전달한다.

밑의 코드로 이전과 비교하며 이해해보자.

const add1 = (a,b) => a+b;
const add2 = (a,b,c) => a+b+c;

console.log(add1(1,2))
console.log(add2(1,2,3))

기존 javascript에서는 받을 인자의 개수를 명시적으로 지정하여 함수를 구현했었다면
ES6의 Rest parameter는 인자의 개수에 상관없이 넘겨주는 인자들을 모두 모아 배열로 받을 수 있게 되었다. 따라서 간결하고 유동적인 코드구현이 가능하다.

const add1 = (...rest) => {

console.log(Array.isArray(rest)); // true

let sum = 0
for(const a of rest) {
    sum+=a
}
return sum;
};

console.log(add1(1,2))     // 3
console.log(add1(1,2,3))   // 6
console.log(add1(1,2,3,4)) // 10

단일 인자와 섞어서 사용하기

Rest 파라미터와 단일 인자들과 섞어서 구현도 가능하다.

앞의 파라미터부터 순서대로 받고 나머지를 Rest 파라미터로 받는다.

function(param1, param2, ...rest){
    ...
}

밑의 코드를 보면 앞에서부터 인자값을 순서대로 받고 나머지를 Rest 파라미터로 모아 배열로 담는다. 만약에, 나머지 인자값이 없다면 빈 배열로 전달한다.

const test = (param1, param2, ...rest) => {
        console.log(`param1 : ${param1}`);
        console.log(`param2 : ${param2}`);
        console.log(`rest : ${rest}`);
    };



/*
 * param1과 param2만 받으며 Rest 파라미터는 빈 배열이다.
 * param1 : 1
 * param2 : 2
 * rest :
 */
test(1,2)

/*
 * param1과 param2를 받고 나머지 3에 대해서만 Rest 파라미터로 전달
 * param1 : 1
 * param2 : 2
 * rest : 3
 */
test(1,2,3)

/*
 * param1과 param2를 받고 나머지 3,4에 대해서만 Rest 파라미터로 전달
 * param1 : 1
 * param2 : 2
 * rest : 3 4
 */
test(1,2,3,4)

arguments VS Rest Parameter

ES5에서도 arguments객체를 통해 가변 인자 함수를 구현할 수 있다.

arguments는 Array 형태의 객체이고 Rest 파라미터는 배열이다.

Array 형태의 객체란, arguments가 length 속성과 더불어 0부터 인덱스 된 다른 속성을 가지고 있지만, Array의 forEach, map과 같은 내장 메서드를 가지고 있지 않다는 뜻이다.

function test(){
    console.log(arguments)
};

test(1,2) // { '0': 1, '1': 2 }

그렇다면, argumentsRest Parameter의 차이점은 무엇인가?

간단하게 말하면

  • arguments는 Array 형태의 객체이기 때문에 배열 내장 함수들을 사용할 수 없다.
  • arguments는 arrow function(화살표 함수)에 바인딩 되지 않는다. 즉, 화살표 함수에 사용할 수 없다.
  • 따라서, 대부분의 경우에는 Rest Parameter가 더 좋은 대안으로 사용되고 있다.
const test = ()=> {
    console.log(arguments)
};

test(1,2) // Uncaught ReferenceError: arguments is not defined

Rest Parameter는 항상 제일 마지막 매개변수여야 한다.

제목그대로 Rest파라미터는 항상 제일 마지막 파라미터로 사용되어야 한다. 그 이유는 나머지를 전부 rest로 사용하기 때문에 뒤에 파라미터가 있다면 할당할 수 없기 때문이다.

const test=(...rest, param1, param12) => {
    console.log(rest);
    console.log(param1);
    console.log(param12);
}

test(1,2,3,4,5) // Uncaught SyntaxError: Rest parameter must be last formal parameter


정리해서

  • Spread 연산자는 배열, 문자열, 객체 등 반복 가능한 이터러블 객체들을 개별 요소로 분리하여 사용할 수 있다.
  • Spread 연산자를 이용해 복제하게되면 새로운 객체를 생성하여 원본 객체를 참조하지 않는다.
  • Rest Parameter는 함수 선언문의 파라미터에 Spread(…) 연산자를 이용하여 작성하여 가변 인자들을 모아 배열로 전달받는 것이다.
  • Rest Parameter는 단일 인자들과 섞어서 사용할 수 있다. 이를 통해 유연한 가변 인자 함수를 구현할 수 있다.
// 예제 1
const test = (x, ...rest) => {
    console.log(x); // 1
    console.log(rest); // (2) [2,3]
}
test(...[1, 2, 3]);


// 예제 2
const test = (x,y,z) => {
    console.log(x); // 1
    console.log(y); // 2
    console.log(z); // 3
}
test(...[1, 2, 3]);

// 예제 3
const test = (x, ...rest) => {
    console.log(x); // first param
    console.log(rest); // (3) [1, 2, 3]
}
test('first param', 1, 2, 3);