나머지 연산자 vs 전개 연산자
자바스크립트 개발을 할 때 ' ... ' 연산자는 매우 유용하게 쓰인다. 쓰임에 따라서 나머지 연산자 혹은 전개 연산자라고 불리는데, 말 그대로 나머지를 가져오거나 어딘가에 배열이나 객체를 펼쳐놓을 때 많이 썼다. 쓸 줄도 알고 이름도 알았지만 이렇게 분류해서 생각해본 적은 처음이다. 그냥 같은 것이라고 생각만하고 썼는데, 직접 분류하려고 보니 명백하게 다른 쓰임새를 갖고 있었다.
그래서 한번 정리를 해보려한다.
Rest syntax (나머지 구문)
Rest Parameter 와 Rest Property로 나누어 진다. 이름 그대로 생각하면 된다. 전자는 매개변수이고 후자는 객체의 프로퍼티이다.
rest parameter (나머지 매개변수)
매개변수의 이름 앞에 ... 를 붙여서 정의한 변수를 말한다. 이 파라미터에는 해당 함수를 호출 할 때 전달 된 인수들의 배열이 할당된다.
function func(...rest) {
console.log(rest) // [1, 2, 3, 4];
}
func(1, 2, 3, 4);
연산자의 이름이 '나머지' 인 이유는 이름을 붙여서 받을 인수들은 받고, 이름 붙인 인수들을 제외한 나머지(뒷부분) 인수들을 배열로 받기 때문이다. 근데 나머지 는 반드시 뒤에 남은 인수들에 대해서만 적용이 가능하다.
function func(a, b, ...rest) {
console.log(a); // 1
console.log(b); // 2
console.log(rest); // [3, 4];
}
func(1, 2, 3, 4);
function func2(...rest, c, d) { // 나머지 연산자는 이름을 붙일 매개변수를 제외하고 뒤에 남은 인수들에게만 적용된다.
console.log(c); // 에러...
console.log(d);
console.log(rest);
}
func2(1, 2, 3, 4);
function func3(a, b, c, d, ...rest) {
console.log(a, b, c, d) // 1 2 3 4
console.log(rest) // [] 나머지로 가져올 인수가 없으면 그냥 빈 배열이 만들어진다.
}
func3(1, 2, 3, 4);
arguments vs Rest parameter
- arguments는 함수 호출시에 전달한 모든 인수를 포함하는 유사배열 객체이다. 반면 나머지 연산자는 직접 이름을 붙인 매개변 수를 제외한 남은 인수들을 rest parameter에 할당한다.
- arguments는 배열이 아니기 때문에 Array method (forEach, map ...)를 사용하려면 번거로운 과정이 필요하다.
- Rest parameter는 배열 인스턴스이기 떄문에 array method가 사용가능하다.
function func(...rest) {
rest.pop() // 정상 작동
}
func(1, 2, 3, 4);
function func2() {
const args = arguments;
const realArr = Array.prototype.slice.call(arguments);
const realArr2 = Array.from(arguments);
realArr.pop() // 정상 작동
arguments.pop() // Error arguments.pop is not a function
}
func2(1, 2, 3, 4);
Rest parameter는 해체가 가능하다.
function func(...[a, b, c, d]) {
return a + b + c + d;
]
console.log(func(1, 2, 3, 4)); // 10
위와 같이 배열이기 때문에 '구조 분해 할당'으로 해체가 가능하다.
Rest property (나머지 프로퍼티)
나머지 프로퍼티는 위의 파라미터에 대한 설명을 이해했다면 간단하다. 배열과 같이 객체를 destructure(구조 분해 할당) 할 때 가져갈 것은 가져가고 남은 프로퍼티들을 객체에 넣어서 통째로 가져온다. 코드를 보자
function func({ a, ...otherProps }) {
console.log(a) // 1
console.log(otherProps) // { b: 2, c: 3 }
}
func({ a: 1, b: 2, c: 3 })
function func2({ ...otherProps, a }) { // 파라미터와 마찬가지로 나머지 연산자는 마지막에 있어야 한다.
console.log(a) // Error! Rest element must be last element
console.log(otherProps)
}
func2({ a: 1, b: 2, c: 3 })
Spread syntax (전개 구문)
Spread 문법은 말 그대로 전개, 펼친다는 것이다. iterable한 배열이나 문자열 등과 같은 대상을 개별 요소로 분리해서 펼친다.
마찬가지로 ... 구문을 쓴다.
console.log(...[1, 2, 3]) // 1 2 3
console.log(...'apple') // a p p l e
함수의 호출에 사용할 때
function func(a, b, c) {
console.log(a, b, c) // 1 2 3
}
// 전개 연산자는 배열을 각각 개별의 요소로 분리하여 a, b, c에 각각 할당 된다.
func(...[1, 2, 3]);
function func2(a, b, c, d, e) {
console.log(a, b, c, d, e); // 1 2 3 4 5
}
// 이런 희한한 것도 가능하다.
func2(...[1, 2], 3, 4, ...[5])
배열에 사용할 때
const array = [4, 5, 6];
// 배열 안에다가 array의 원소를 전개한다. 배열에 배열을 이어 붙인 것과 같은 효과!
console.log([1, 2, 3, ...array]); // [1, 2, 3, 4, 5, 6];
// 빈 배열에다가 array를 전개하면 새로운 배열에 4, 5, 6 원소가 들어가게 된다.
// 배열 전체를 slice 한 것과 같은 결과를 얻을 수 있다.
console.log([...array]) // [4, 5, 6]
// 원래 배열의 push 메서드를 사용할 때는 원소 각각을 인수로 넣어줘야한다.
array.push(1, 2, 3);
console.log(array) // [4, 5, 6, 1, 2, 3]
// 전개 연산자를 이럴 때 사용할 수 있다.
const array2 = [1, 2, 3];
array.push(...array2);
console.log(array) // [4, 5, 6, 1, 2, 3]
객체에 사용할 때
객체는 사실 iterable 하지 않기 때문에 Spead syntax를 적용할 수 없다. 하지만 Spead Property 일땐 얘기가 다르다.
const obj1 = { a: 1 };
const obj2 = { b: 2, c: 3 };
const obj3 = { ...obj1, ...obj2 };
console.log(obj3) // { a: 1, b: 2, c: 3 };
key-value 쌍을 객체 내에 전개하는 것이 가능하다. 따라서 객체끼리 합치거나, 새로운 객체에 이전 객체의 내용을 옮길 때 매우 쉽게 옮길 수 있다.
Spread 구문을 쓸 때 주의할 점이 있다. 바로 함수 호출에 사용할 때 함수의 인자 갯수에 대한 주의사항인데, 자바스크립트 코어 엔진에서는 65536개가 한계이다. 이는 엔진마다 다르기 때문에 정해진 부분이 없다. 대략적으로 몇만개 이상의 인자를 spread 할 때는 주의해야한다. 이 부분에 대해서는 apply 문서를 보면 좋을 듯하다.
Rest syntax와 Spread syntax를 정리해보면
속성 | Rest(나머지) | Spread(전개) |
용도 | 여러 엘리먼트를 수집하며 이를 하나의 엘리먼트로 '압축' | 전개는 iterable한 데이터를 그 엘리먼트로 '확장' |
사용법 | 필요한 데이터에 이름을 붙이고 남은 것들에 대해 (a, b, ...name) 으로 압축한다. | 다른 데이터에다가 ... 구문으로 데이터를 확장한다. [1, 2, ...[3, 4, 5]] |
프로퍼티 | 객체 구조분해 할당을 할 때 ({ a, b, ...otherProps }) 와 같이 otherProps에 Rest property를 저장한다. | 객체에다가 다른 객체를 확장 할 때 { a: 1, ...obj2 } 처럼 Spread Property를 전개하여 확장한다. |
틀린 부분이 있다면 지적 부탁드립니다.
참조
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/Spread_syntax
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Functions/rest_parameters
https://poiemaweb.com/es6-extended-parameter-handling
'programming > javascript' 카테고리의 다른 글
자바스크립트 this (0) | 2021.06.10 |
---|---|
자바스크립트 클로저(Closure) (0) | 2021.06.08 |
[webpack] 오래된 글을 보고 웹팩 설정을 하다가 바뀐 부분이 많아 고생했던 일 (0) | 2018.11.18 |
[jquery] offset (0) | 2018.03.28 |
[ES6] 클래스(class) (0) | 2018.02.06 |