프론트엔드/JavaScript

[JavaScript] Spread와 Rest의 차이

ES6에서 도입된 SpreadRest 문법은 똑같이 생겼다.

다만 쓰이는 상황에 따라서 둘을 구분하기 때문에 확실하게 정리해보고자 한다.


1. Spread (전개 구문)

Spread는 배열, 문자열 등의 Iterable 한 요소들을 개별 요소로 분리할 수 있다.

주로 객체나 배열의 연결, 복사 등의 용도로 유용하게 쓰인다.

 

(참고로 객체는 Iterable이 아니지만 ES9부터 Spread 사용이 가능해졌다.)

 

1) 배열 결합

기존엔 두 배열을 합치는 데에 concat()를 이용했었다.

하지만 ES6에선 Spread 연산자를 이용하여 좀 더 쉬게 배열을 합칠 수 있다.

// ES5 - concat()
var arr1 = [1, 2, 3];
var arr2 = [4, 5, 6];

var arr = arr1.concat(arr2); // [ 1, 2, 3, 4, 5, 6 ]


// ES6 - Spread
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];

const arr = [...arr1, ...arr2]; // [ 1, 2, 3, 4, 5, 6 ]

 

또한 Spread는 다양한 형태의 배열 결합도 간단하게 구현할 수 있다.

const arr1 = [1, 2];
const arr2 = [0, ...arr1, 3, 4];

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

 

2) 배열 복사

JavaScript는 배열을 새로운 변수에 할당하면 기존 배열을 참조하게 된다.

그래서 새로운 배열을 변경하면 원본 배열 역시 변경된다.

 

배열 참조가 아닌 배열 복사를 원한다면 기존에는 slice() 또는 map()을 사용하였다.

그러나 Spread 연산자를 사용하면 쉽게 배열을 복사할 수 있다.

 

(참고로 Spread는 shallow copy이다.)

// ES5 - slice()
var arr1 = [1, 2, 3];
var arr2 = arr1.slice();

// ES5 - map()
var arr1 = [1, 2, 3];
var arr2 = arr1.map((item) => item);

// ES6 - Spread
const arr1 = [1, 2, 3];
const arr2 = [...arr1];

 

3) 객체에서의 Spread Operator

원래 객체는 Iterable이 아니라서 Spread를 사용하면 에러가 발생했다.

const obj = { name: 'John', age: '20' };

console.log(...obj); // TypeError: obj is not iterable

 

하지만 ES2018(ES9)에서 객체와 관련된 Spread Opeator가 추가되었다.

const obj1 = { name: 'John', age: '20' };
const obj2 = { name: 'Tom', age: '23' };

const clonedObj = { ...obj1 };
// Object { name: 'John', age: '20 }

const mergedObj = { ...obj1, ...obj2 };
// Object { name: 'John', age: '20', name: 'Tom', age: '23' }

 

4) Immutable (불변성)

Spread 연산자는 특히 React에서 불변성(immutable) 유지하는 데에 자주 사용된다.

Spread를 통해 객체를 업데이트하면 얻을 수 있는 장점은 다음과 같다.

  • newObj가 obj와 서로 다르다는 결과를 통해 상태 변화를 감지할 수 있다.
  • obj의 상태가 변경되지 않아 원본의 불변성을 유지할 수 있다.
const obj = { name: 'John', age: '20', flag: 'true', foo: 'bar' };
const newObj = { ...obj1, name: 'Alice' };
// Object { name: 'Alice', age: '20', flag: 'true', foo: 'bar' }

console.log(obj === newObj); // false

2. Rest (나머지 매개변수)

1) 함수의 Rest Parameter

기존 JavaScript에서는 함수의 매개변수로 배열을 받으려면 arguments라는 유사 배열을 활용했다.

하지만 ES6의 Rest Parameter를 이용하면 함수의 매개변수들을 쉽게 배열로 만들 수 있다.

 

(단 Rest parameter는 항상 제일 마지막에 위치해야 한다.)

// ES5 - arguments
function add() {
  var i = 0;
  var sum = 0;

  for (i = 0; i < arguments.length; i++) {
    sum += arguments[i];
  }

  return sum;
}

console.log(add(1, 2, 3, 4)); // 10


// ES6 - Rest Parameter
function add(...rest) {
  let sum = 0;

  for (let item of rest) {
    sum += item;
  }

  return sum;
}

console.log(add(1, 2, 3, 4)); // 10

 

2) 함수 호출 인자로 사용

기존에는 배열 형태를 함수의 인자로 전달하려면 직접 풀거나 apply()를 사용했다.

하지만 Spread operator를 이용하면 배열 형태를 바로 함수 인자로 전달할 수 있습니다.

// ES5 - apply()
var args = [1, 2, 3, 4];
Math.max.apply(Math, args); // 4

// ES6 - Spread
const args = [1, 2, 3, 4];
Math.max(...args); // 4

 

3) Destructuring

Spread는 Destructuring(구조 분해 할당)에서 응용될 수 있다.

Destructuring에 대해 자세히 알고싶으면 아래 포스팅을 참고하면 된다.

[a, b, ...rest] = [10, 20, 30, 40, 50];

console.log(rest); // [30, 40, 50]
 

[ES6] Destructing (구조 분해 할당)

Destructuring JavaScript로 개발을 하다 보면 함수에 객체나 배열을 전달하는 경우가 생긴다. 또 모든 데이터가 아닌 일부만 필요한 경우도 자주 생긴다. 이럴 때 Destructing(구조 분해 할당)을 통해 객체

seokzin.tistory.com

 

'프론트엔드 > JavaScript' 카테고리의 다른 글

[JavaScript] this  (2) 2022.01.05
[JavaScript] Prototype  (0) 2022.01.05
[JavaScript] var, let, const 차이  (0) 2021.12.14
[JavaScript] 구조 분해 할당 (Destructing)  (0) 2021.12.09
[JavaScript] axios와 fetch 차이  (0) 2021.12.09