자바스크립트 - 스프레드 연산자(Spread Operator)와 나머지 매개변수(Rest Parameter) & 구조 분해 할당(Destructuring Assignment)
Spread Operator는 ES6에서 추가된 문법으로,
'...' <- 이런 모습을 하고 있습니다.
mdn에서는 아래와 같이 정의 됩니다.
배열이나 문자열과 같이 반복 가능한 문자를 0개 이상의 인수 (함수로 호출할 경우) 또는 요소 (배열 리터럴의 경우)로 확장하여, 0개 이상의 키-값의 쌍으로 객체로 확장시킬 수 있습니다.
이해가 잘 되지 않아 쓰임새를 살펴보며 공부하였습니다.
1. 함수 인자 전달 시 : function(...element) {}
Rest parameter
함수의 매개변수(parameter)를 spread operator로 작성한 형태를 Rest parameter라고 부릅니다. Rest 파라미터를 사용하면 함수의 인자, 매개변수로 오는 값들을 "배열"로 전달 받을 수 있습니다. 함수의 마지막 매개변수 앞에 "..." 를 붙이면 (사용자가 제공한) 모든 후속 매개변수를 배열에 넣도록 지정합니다.
- 함수 정의에는 하나의 ...만 존재할 수 있습니다.
- 마지막 매개변수만 나머지 매개변수(rest parameter) 로 설정할 수 있습니다.
- Rest parameter는 Array 인스턴스이므로 sort, map, forEach, pop 등의 메서드를 직접 적용할 수 있습니다.
1) 인자로 전달받지 못한 것들을 배열로 담아둔다.
function checkNums(first, second, ...nums) {
console.log(first);
console.log(second);
console.log(nums);
}
checkNums(1, 2, 0, 1, 2, 4);
// first : 1, second : 2, nums : [0,1,2,4]
checkNums(1, 2, 3);
// first : 1, second : 2, nums : [3]
checkNums(1, 2);
// first : 1, second : 2, nums : []
2) 인수를 배열로 만들기.
// 나머지 매개변수 이전에 "arguments"를 일반 배열로 변환하던 방법
function f(a, b) {
let normalArray = Array.from(arguments)
let first = normalArray.shift() // 동작, 첫 번째 매개변수 반환
let first = arguments.shift() // 오류, arguments는 실제 배열이 아님
}
// 이제는 나머지 매개변수를 사용해 쉽게 배열로 가져올 수 있음
function f(...args) {
let normalArray = args
let first = normalArray.shift() // 동작, 첫 번째 매개변수 반환
}
3) 결과값이 배열이므로, 배열의 length를 알 수 있고 array 프로토타입 메소드이용이 가능합니다. sort, map, forEach, pop. Arguments 객체에서는 배열의 프로토타입 메소드를 사용할 수 없습니다.
function fun1(...theArgs) {
console.log(theArgs.length)
}
fun1() // 0
fun1(5) // 1
fun1(5, 6, 7) // 3
function sortRestArgs(...theArgs) {
let sortedArgs = theArgs.sort()
return sortedArgs
}
console.log(sortRestArgs(5, 3, 7, 1)) // 1, 3, 5, 7
function sortArguments() {
let sortedArgs = arguments.sort()
return sortedArgs
}
console.log(sortArguments(5, 3, 7, 1))
// TypeError 발생 (arguments.sort is not a function)
2. 배열의 생성 : […element]
이미 존재하는 배열을 일부로 하는 새로운 배열의 생성이 전개 구문을 통해 간결해 졌습니다.
함수에서 인수 목록을 위한 spread operator처럼 '...' 은 배열 리터럴의 어디에서든 사용될 수 있으며 여러번 사용될 수 도 있습니다.
var parts = ['shoulders', 'knees'];
var lyrics = ['head', ...parts, 'and', 'toes'];
// ["head", "shoulders", "knees", "and", "toes"]
유사배열을 배열화 하는데도 용의합니다. 개인적으로 자주 사용했던 부분은, queryselectorAll을 사용하여 여러 node들을 NodeList로 가져왔을 때, 유사배열이기 때문에 안되는 배열 메소드(find, filter..)들을 실행하기 위해 배열화 시켜줄때 이용하곤 했습니다.
<div class="test">1</div>
<div class="test">2</div>
<div class="test">3</div>
<div class="test">4</div>
<div class="test">5</div>
안되는 사례
<script>
const tests = document.querySelectorAll(".test");
console.log(tests); // NodeList(5) : 유사배열, array가 아님.
tests.find((number) => { // 에러 tests.find is not a function. Array method 안됨.
number.innerHtml > 4;
});
console.log(tests);
</script>
되게 하기
<script>
const tests = document.querySelectorAll(".test");
const newTests = [...tests]; // 유사배열을 배열로 만들기. NodeList -> Array
console.log(newTests); // array(5)
console.log(newTests.find((number) => {
return number.innerHTML > 4;
})); // <div class="test"> 5 </div>
</script>
배열 복사 (Deep copy)
Spread 문법은 배열을 복사할 때 1 레벨 깊이(Deep copy)로 동작합니다. (Not shallow)
var arr = [1, 2, 3];
var arr2 = [...arr]; // arr.slice() 와 유사
arr2.push(4);
// arr2 은 [1, 2, 3, 4] 이 됨
// arr 은 영향을 받지 않고 남아 있음
배열안의 object의 경우 즉 1단계가 넘어가는 경우 shallow copy로 돌아갑니다. 1레벨 이상의 깊이의 경우와 같은 다차원 배열을 복사하는 것에는 적합하지 않을 수 있습니다.
var arr1 = [{name: '철수', age: 10}];
var arr2 = [...arr1];
arr2[0].name = '영희';
console.log(arr1); // [ {name:'영희', age: 10}]
console.log(arr2); // [ {name:'영희', age: 10}]
3. 오브젝트 생성시 : {…obj} 2018 EcmaScript 2018
ES2018 (ES9)에서는 객체와 관련된 사항이 추가되었습니다. 최근의 브라우저는 객체에 대한 Spread Operator 역시 지원합니다.
객체 복사 또는 업데이트
객체에서 spread operator를 이용하여 객체의 복사 또는 프로퍼티를 업데이트 할 수 있습니다.
아래 두번째 예제는 객체의 프로퍼티를 오버라이드 함으로써 객체가 업데이트되는 것을 이용한 내용입니다.
배열과 마찬가지로, 오브젝트 안의 오브젝트의 경우(1단계 이상의 복사의 경우) shallow copy가 됩니다.
var obj1 = { foo: 'bar', x: 42 };
var obj2 = { foo: 'baz', y: 13 };
var clonedObj = { ...obj1 };
// Object { foo: "bar", x: 42 }
var mergedObj = { ...obj1, ...obj2 };
// Object { foo: "baz", x: 42, y: 13 }
Destructuring Assignment (구조 분해 할당)
구조를 분해해서 할당하는 것.
구조 분해 할당 구문은 배열이나 객체의 속성을 해체하여 그 값을 개별 변수에 담을 수 있게 하는 JavaScript 표현식입니다.
1) 배열 활용
const nums = [1,2,3]
function add(a, b, c) {
console.log(a+b+c)
};
이때 nums를 하나하나 add라는 함수에 전달하려면 어떻게 해야 할까?
add(nums[0], nums[1], nums[2]); // 번거롭다.
add(…nums); ==>> 이렇게 하면 자동으로 전달이 된다.
코드상에서 더 의미 있는 이름을 분배하고 싶을 때.
const importanceOrder = [“음악”, “코딩”, “운동”]
const [1순위, 2순위, 3순위] = importaceOrder;
console.log{1순위); // 음악.
importaceOrder에 값이 없다면, 기본값도 설정 가능. // (default parameter)
const [1순위, 2순위, 3순위, 4순위 = “없음”] = importaceOrder
console.log(4순위) // 없음.
function createEmoji() {
return [‘apple’, ‘사과’];
}
const array = createEmoji(); 이렇게 해도 괜찮지만, 의미있는 이름으로 받아오려면
const [“사과영문”, “사과한글”] = createEmoji(); 이렇게 하면 된다.
2) 오브젝트 활용
const ellie = {name : “ellie’, age: 20, job :”eng”} ;
function display(person) {
console.log( “이름” + person.name);
console.log(“나이” + person.age);
}
//원래 이렇게 했다면. 이제는 처음에 받아올때 부터 구조를 분해해서 받아올 수 있다.
function display({name, age, job}) {
console.log( “이름” + name);
console.log(“나이” + age);
}
const {name, age, job} = ellie; 이렇게도 쓸 수 있다.
console.log(name) // ellie // 오브젝트 안의 내용들이 각각 변수로 선언이 된 것.
console.log(age) // 20
const {name, age, job:occupation, pet = “강아지”} = ellie;
job이라는 key 대신에 occupation을 쓰고 싶은 경우.
pet 이라는 key안에 값이 없을 경우 강아지 사용하고 싶을 때
참조
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
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://academy.dream-coding.com/courses/javascript
http://poiemaweb.com/es6-extended-parameter-handling