전개 구문(Spread Syntax)
'...'
배열이나 문자열과 같이 반복 가능한 문자를 0개 이상의 arguments(for function calls)
또는 0개 이상의 elements(for array literals)로 확장하는 구문
<!DOCTYPE html>
<html>
<body>
<script>
function sum(x, y, z) {
return x + y + z;
}
const numbers = [1, 2, 3];
console.log(sum(...numbers));
// Expected output: 6
console.log(sum.apply(null, numbers));
// Expected output: 6
</script>
</body>
</html>
일반적으로 배열의 element를 함수의 인수로 사용하고자 할 때, apply()를 사용했지만(13줄) 전개구문을 사용하면 11줄과 같이 표현할 수 있다. 참고로 인수 목록의 모든 인수는 전개 구문을 사용할 수 있으며, 여러번 사용될 수도 있다.
전개구문 덕에 다음과 같이 배열을 new와 함께 쉽게 사용할 수도 있게 되었다.
var dateFields = [1970, 0, 1]; // 1 Jan 1970
var d = new Date(...dateFields);
배열의 얕은 복사에서도 유용하게 사용된다.
var arr = [1, 2, 3];
var arr2 = [...arr]; // arr.slice() 와 유사
arr2.push(4);
// arr2 은 [1, 2, 3, 4] 이 됨
// arr 은 영향을 받지 않고 남아 있음
이미 존재하는 배열의 끝에 배열을 이어붙이는데 보통 concat()을 사용하는데, 전개구문을 쓸 수도 있다.
var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
arr1 = arr1.concat(arr2); // arr2 의 모든 항목을 arr1 에 붙임
arr3 = [...arr1, ...arr2]; // arr3는 [0, 1, 2, 3, 4, 5]
나머지 매개변수 구문 (Rest Parameter Syntax)
'...'
전개 구문과 형식은 똑같지만, 배열을 '확장'하는 전개 구문과 달리
여러 elements를 모아 하나의 element로 '압축'한다.
나머지 매개변수 구문은 함수에서 정해지지 않은 수의 매개변수를 배열로 받을 때 사용한다.
가변항 함수(variadic functions)를 표현할 때 사용한다.
function sum(...theArgs) {
let total = 0;
for (const arg of theArgs) {
total += arg;
}
return total;
}
console.log(sum(1, 2, 3));
// Expected output: 6
console.log(sum(1, 2, 3, 4));
// Expected output: 10
함수의 마지막 매개변수 앞에 '...'을 붙이면 사용자가 입력한 모든 후속 매개변수를 표준 JavaScript 배열에 넣는다.
밑의 예시와 같이 마지막 매개변수만 나머지 매개변수로 설정할 수 있음에 주의하자.
여기서 매개변수를 딱 세 개만 제공하더라도 마지막 인수는 배열이다. (요소가 하나지만 배열이다.)
(매개변수를 두 개만 제공하면 마지막 인수는 빈 배열.)
구조 분해 할당 (Destructuring Assignment)
배열이나 객체의 속성을 해체하여 그 값을 개별 변수에 담을 수 있게 하는
JavaScript 표현식
함수에 객체나 배열을 전달해야하는 경우, 특히 데이터 전체가 아닌 일부만 필요한 경우 구조 분해 할당을 사용한다. 또한 함수의 매개변수가 많거나 매개변수 기본값이 필요한 경우에 유용하다.
배열 분해와 객체 분해로 나누어 알아보자.
let a, b, rest;
[a, b] = [10, 20];
console.log(a);
// Expected output: 10
console.log(b);
// Expected output: 20
[a, b, ...rest] = [10, 20, 30, 40, 50];
console.log(rest);
// Expected output: Array [30, 40, 50]
우선 배열 분해부터 살펴보겠다. 기본적으로 배열이 어떻게 변수로 분해되는지 아래 예제를 보자.
// 이름과 성을 요소로 가진 배열
let arr = ["Bora", "Lee"]
// 구조 분해 할당을 이용해 firstName엔 arr[0], surname엔 arr[1]을 할당
let [firstName, surname] = arr;
alert(firstName); // Bora
alert(surname); // Lee
이처럼 인덱스를 이용해 배열에 접근하지 않고도 변수로 이름과 성을 사용할 수 있다.
배열 분해의 다양한 예시들을 살펴보겠다.
//예시1
let [firstName, surname] = "Bora Lee".split(' ');
//예시2
// 두 번째 요소는 필요하지 않음
let [firstName, , title] = ["Julius", "Caesar", "Consul", "of the Roman Republic"];
alert( title ); // Consul
//예시3
let user = {};
[user.name, user.surname] = "Bora Lee".split(' ');
alert(user.name); // Bora
//예시4
let [a, b, c] = "abc"; // ["a", "b", "c"]
let [one, two, three] = new Set([1, 2, 3]);
//예시5
let guest = "Jane";
let admin = "Pete";
// 변수 guest엔 Pete, 변수 admin엔 Jane이 저장되도록 값을 교환함
[guest, admin] = [admin, guest];
alert(`${guest} ${admin}`); // Pete Jane
예시 1) split과 같이 반환값이 배열인 메서드를 함께 활용할 수도 있다.
예시 2) 쉼표를 사용하면 필요하지 않은 배열 요소를 버릴 수 있다. ("Caesar"는 건너뛰고 title에 Consul이 할당되었다.)
예시 3) 할당 연산자 좌측엔 할당할 수 있는(assignables) 것이라면 어떤 것이든 올 수 있다. (위 예시는 객체 프로퍼티가 온 경우)
예시 4) 할당 연산자 우측엔 모든 반복 가능한 객체(iterable)가 올 수있다. (위 예시는 set가 온 경우)
예시 5) 변수에 저장된 값을 교환할 때 배열 분해 방식을 사용할 수 있다.
구조 분해 할당으로 배열뿐만아니라 객체도 분해할 수 있는데, 이제 객체 분해해 대해 살펴보자.
다음 예시처럼 할당 연산자 우측에 분해하고자 하는 객체를 넣고, 좌측에 상응하는 객체 프로퍼티의 패턴을 넣으면 된다. (title, width, height의 순서는 상관없다.)
let options = {
title: "Menu",
width: 100,
height: 200
};
let {title, width, height} = options;
alert(title); // Menu
alert(width); // 100
alert(height); // 200
여기서 options.width를 w라는 변수에 저장하고 싶으면 어떻게 수정해야할까?
let options = {
title: "Menu",
width: 100,
height: 200
};
// { 객체 프로퍼티: 목표 변수 }
let {width: w, height, title} = options;
// width -> w
alert(title); // Menu
alert(w); // 100
alert(h); // 200
이처럼 콜론(:)을 사용해 {객체 프로퍼티: 목표 변수} 형태로 사용하면된다.
프로퍼티가 없는 경우는 =을 사용해 기본값을 설정할 수도 있다.
let options = {
title: "Menu"
};
let {width = 100, height = 200, title} = options;
alert(title); // Menu
alert(width); // 100
alert(height); // 200
let options = {
title: "Menu"
};
let {width = prompt("width?"), title = prompt("title?")} = options;
alert(title); // Menu
alert(width); // prompt 창에 입력한 값
마지막으로 구조 분해는 함수 매개변수가 많은데, 선택적으로 쓰일 때 유용하다. 예시를 통해 무슨 의미인지 살펴보자.
메뉴 생성에 관여하는 함수 showMenu는 메뉴의 너비, 높이, 제목, 항목 리스트를 매개변수로 받는다.
function showMenu(title = "Untitled", width = 200, height = 100, items = []) {
// ...
}
// 기본값을 사용해도 괜찮은 경우 아래와 같이 undefined를 여러 개 넘겨줘야한다.
showMenu("My Menu", undefined, undefined, ["Item1", "Item2"])
이렇게 함수 작성시 넘겨주는 인수의 순서가 틀려 문제가 발생할 수도 있고, 매개변수에 기본값이 설정되어 굳이 인수를 넘겨주지 않아도 되는 경우에도 위와 같이 undefined를 여러 개 넘겨줘야한다.
이럴 때, 다음과 같이 구조 분해를 이용하여 가독성을 높이고, 오류 발생을 줄일 수 있다.
// 함수에 전달할 객체
let options = {
title: "My menu",
items: ["Item1", "Item2"]
};
//전달받은 객체를 분해해 변수에 즉시 할당함
function showMenu({title = "Untitled", width = 200, height = 100, items = []}) {
// title, items – 객체 options에서 가져옴
// width, height – 기본값
alert( `${title} ${width} ${height}` ); // My Menu 200 100
alert( items ); // Item1, Item2
}
showMenu(options);
지금까지 전개 구문(Spread syntax), 나머지 매개변수 구문(Rest parameter syntax), 구조 분해 할당(Destructing assignment)에 대해 알아봤다.
이들은 iterator와 밀접한 관련이 있는데, 이에 대한 글은 아래에 첨부하겠다.
[JavaScript] #2 제너레이터와 이터레이터 (함수형 프로그래밍과 JavaScript ES6+)
제너레이터(Genarator) 이터레이터(iterator)이자 이터러블(iterable)을 생성하는 함수 genarator를 생성할 때는 함수 생성과 동일하게 하는데, *을 사용한다. 11라인을 보면 iter은 이터레이터이자 symbol.itera
r-ing.tistory.com