단축 속성(shorthand property)은 { name, price }처럼 이름만 늘어놓아, 같은 이름이 가리키는 값으로 속성을 만드는 꼴입니다. 구조 분해에 나머지 속성(rest property) ...rest를 쓰면 꺼내고 남은 속성이 새 객체로 모이고, 나머지 매개변수(rest parameter) ...items는 남은 인자를 새 배열로 모읍니다.
구조 분해와 거꾸로, 이름 여럿을 객체에 담으려면 { name: name, price: price }처럼 같은 낱말을 두 번씩 써야 합니다. 몇 개가 올지 모르는 인자는 매개변수를 미리 늘어놓을 수도 없습니다.
이 레슨에서는 이름을 한 번만 쓰는 단축 속성과 남은 것을 모으는 두 꼴을 보고, ...rest가 무엇을 가리키는지 그림에서 먼저 맞혀 봅니다.
1단축 속성: 이름이 곧 키
그림 앞 장면의 코드에 찍는 줄을 더했습니다.
const name = '커피';
const price = 4000;
const item = { name, price };
console.log(item);
{ name: '커피', price: 4000 }3번 줄의 { name, price }는 객체를 만듭니다. 키 name에는 이름 name이 가리키는 값을, 키 price에는 이름 price가 가리키는 값을 담습니다. { name: name, price: price }를 줄여 쓴 것이라 이름이 곧 키가 됩니다. 자료에 따라 프로퍼티명 약식이라고도 부릅니다.
같은 꼴의 중괄호라도 const { name, price } = item;처럼 = 왼쪽에 쓰면 꺼냅니다(구조 분해로 꺼내기). 3번 줄처럼 = 오른쪽에 쓰면 만듭니다.
그림에서 3번 줄은 이름 name과 price가 가리키는 값을 읽어, 키가 같은 두 줄로 객체 ①을 만듭니다.
객체의 줄에는 이름이 들어갈까요, 그때의 값이 들어갈까요? 아래 코드는 객체를 만든 뒤 이름 price를 다시 붙였습니다. 무엇을 찍을지 답을 정한 뒤 펼쳐 보세요.
let price = 4000;
const item = { price };
price = 4500;
console.log(item.price);
실행 결과 보기
4000예상과 다르면 그곳이 배울 곳입니다. 2번 줄은 그때 price가 가리키던 4000으로 줄을 만듭니다(규칙 2). 3번 줄은 이름 price를 4500에 다시 붙일 뿐이고(규칙 3), 객체의 줄은 이름이 아니라 값 4000을 담고 있어 바뀌지 않습니다.
2나머지 속성: 남은 속성을 새 객체로
그림 뒤 장면의 코드에 찍는 줄 둘을 더했습니다.
const item = {
name: '커피',
price: 4000,
hot: true,
};
const { name, ...rest } = item;
console.log(name);
console.log(rest);
커피
{ price: 4000, hot: true }6번 줄은 = 왼쪽이 중괄호라 구조 분해입니다. name은 item의 name 속성을 꺼내 '커피'에 붙습니다. 그 뒤의 ...rest가 나머지 속성입니다. 꺼내고 남은 price와 hot을 새 객체에 담고, 그 객체에 이름 rest를 붙입니다.
... 뒤의 이름은 rest가 아니어도 됩니다. 꺼내도 item은 그대로라서, item을 찍으면 name, price, hot 세 속성이 모두 나옵니다.
남는 속성이 없으면 rest는 빈 객체 {}에 붙습니다.
그림에서 6번 줄은 ①의 name 줄에서 '커피'를 꺼내고, 남은 price와 hot 두 줄을 새 객체 ②에 담습니다. ①의 세 줄은 그대로 남습니다.
나머지 속성은 맨 끝에만 씁니다. 뒤에 꺼낼 이름이 더 오면 멈춥니다.
const item = { name: '커피' };
const { ...rest, name } = item;
console.log(name);
main.js:2const { ...rest, name } = item; ^^^^ SyntaxError: Rest element must be last element오류 메시지는 '...rest는 마지막 자리여야 한다'는 뜻입니다. 문법 오류(SyntaxError)라서 실행하기 전에 멈추고, 1번 줄도 실행되지 않습니다(오류 메시지 읽기).
3나머지 매개변수: 남은 인자를 새 배열로
매개변수 자리에도 ... 기호를 쓸 수 있습니다. 나머지 매개변수는 넘긴 인자가 몇 개든 새 배열 하나에 모아 받습니다.
function count(...items) {
console.log(items);
console.log(items.length);
}
count('커피', '라테');
[ '커피', '라테' ]
25번 줄이 count를 부르면 새 칸이 생기고(규칙 5), 인자 '커피'와 '라테'가 새 배열에 차례로 담겨 items가 그 배열을 가리킵니다. items는 보통의 배열이라 items.length처럼 배열에 쓰던 것을 그대로 씁니다.
앞에 보통 매개변수가 있으면 앞자리부터 하나씩 받고 남은 인자만 모읍니다. 매개변수가 (table, ...items)인 bill을 bill(3, '커피', '라테')로 부르면 table은 3을, items는 ['커피', '라테']를 가리킵니다.
나머지 매개변수도 맨 끝에만 씁니다. (...items, last)처럼 뒤에 매개변수를 더 두면 'Rest parameter must be last formal parameter'(나머지 매개변수는 맨 끝 매개변수여야 한다)라는 SyntaxError로 멈춥니다.
인자를 하나도 넘기지 않으면 items는 무엇에 붙을까요? 무엇을 찍을지 답을 정한 뒤 펼쳐 보세요.
function count(...items) {
console.log(items);
console.log(items.length);
}
count();
실행 결과 보기
[]
0예상과 다르면 그곳이 배울 곳입니다. 모을 인자가 없어도 items는 빈 배열에 붙습니다. 보통 매개변수는 짝이 되는 인자가 없으면 undefined에 붙지만(매개변수와 인자), 나머지 매개변수는 언제나 배열이라 items.length도 멈추지 않고 0입니다.
예제
몇 개든 받아 더하기
넘긴 가격이 몇 개든 모두 더해 돌려주는 함수입니다.
function sum(...prices) {
let total = 0;
for (const p of prices) {
total = total + p;
}
return total;
}
console.log(sum(4500, 2000));
console.log(sum());
6500
08번 줄에서는 prices가 [4500, 2000]을 가리켜, 두 값을 더한 6500을 돌려줍니다.
9번 줄에서는 prices가 빈 배열이라 반복이 한 바퀴도 돌지 않고, total의 처음 값 0을 돌려줍니다(3절의 빈 배열).
자주 틀리는 곳
틀린 생각: 매개변수 자리의 ... 기호도 넘긴 배열을 펼친다.
Math.max(...prices)의 ... 기호는 배열을 인자 자리에 펼쳤습니다(가장 큰 값 찾기). 그러니 매개변수에 쓴 ...items도 넘긴 배열을 칸마다 풀어 받을 것 같습니다.
function count(...items) {
console.log(items);
console.log(items.length);
}
const cart = ['커피', '라테'];
count(cart);
count(...cart);
[ [ '커피', '라테' ] ]
1
[ '커피', '라테' ]
26번 줄은 배열 cart를 인자 하나로 넘깁니다. items는 그 배열 하나를 담은 새 배열이라 길이가 1입니다. 7번 줄처럼 부르는 쪽에서 펼쳐야 인자 둘이 되어 3절과 같은 배열이 모입니다.
맞는 규칙: 같은 ... 기호라도 자리에 따라 하는 일이 다릅니다. 부르는 쪽 인자 자리와 = 오른쪽에서는 펼치고, 매개변수 자리와 = 왼쪽에서는 모읍니다.
틀린 생각: 단축 속성은 값만 담으니, 키는 쓰는 쪽에서 골라 꺼내면 된다.
{ name, cost }에 키를 따로 적지 않았으니, 가격을 item.price로 꺼내도 될 것 같습니다.
const name = '커피';
const cost = 4000;
const item = { name, cost };
console.log(item.price);
undefined3번 줄은 { name: name, cost: cost }와 같습니다. 키가 이름 그대로 cost라서 item에는 price 속성이 없고, item.price는 undefined입니다(규칙 6).
맞는 규칙: 단축 속성의 키는 이름 그대로입니다. 키를 이름과 다르게 하려면 그 속성만 콜론 꼴로 써서 { name, price: cost }처럼 씁니다. 그러면 { name: '커피', price: 4000 }이 됩니다.
틀린 생각: ...rest로 모은 객체는 원래 객체와 아주 따로다.
rest는 새 객체이니, rest 안을 고쳐도 item은 그대로일 것 같습니다.
const item = {
name: '커피',
sizes: ['S', 'M'],
};
const { name, ...rest } = item;
rest.sizes.push('L');
console.log(item.sizes);
[ 'S', 'M', 'L' ]rest는 새 객체이지만, 그 안의 sizes 줄은 item의 sizes 줄과 같은 배열을 가리킵니다. 6번 줄이 그 배열에 'L'을 더해 item에서도 보입니다.
맞는 규칙: 나머지 속성도 바깥 한 겹만 새로 만듭니다. 펼치기 { ...a }와 같습니다(객체를 넘기고 복사하기).
확인 문제
1. 할 일 한 건에서 id를 뺀 나머지를 찍습니다. 무엇이 찍힐까요? 한 줄을 글자 그대로 쓰세요.
const todo = {
id: 1,
text: '청소',
done: false,
};
const { id, ...rest } = todo;
console.log(rest);
힌트
6번 줄에서 id를 꺼낸 뒤 남는 속성이 무엇인지, 그 속성들이 어떤 꼴로 모이는지 봅니다.
정답
{ text: '청소', done: false }6번 줄은 id를 꺼내고, 남은 text와 done을 새 객체에 담아 rest를 붙입니다. 키와 값이 짝을 지은 채로 모입니다.
흔한 오답은 [ '청소', false ]입니다. 나머지 매개변수처럼 배열로 모인다고 본 것입니다. 나머지 속성은 객체에서 꺼내므로 남은 것도 키가 붙은 객체로 모입니다. id까지 넣어 세 속성을 쓴 답은 꺼낸 속성도 함께 모인다고 본 것입니다.
2. 메뉴 이름과 수량을 받아 주문 한 건을 돌려주려 합니다. 찍히는 것이 { item: '라테', qty: 1 }이 되도록, 단축 속성으로 빈칸을 채우세요.
function order(item, qty) {
return ________;
}
console.log(order('라테', 1));
힌트
매개변수 이름 item과 qty가 그대로 키가 되어야 합니다. 찍히는 키의 차례도 봅니다.
정답
{ item, qty }입니다.
function order(item, qty) {
return { item, qty };
}
console.log(order('라테', 1));{ item: '라테', qty: 1 }2번 줄의 중괄호는 return 뒤 값을 쓰는 자리라 객체를 만듭니다. 키 item에는 매개변수 item의 값 '라테'를, 키 qty에는 1을 담습니다.
흔한 오답은 { qty, item }입니다. 두 속성은 같지만 객체는 담은 차례대로 찍혀 { qty: 1, item: '라테' }가 됩니다. { item: '라테', qty: 1 }을 그대로 쓰면 이번에는 맞게 찍히지만, order('커피', 2)로 불러도 같은 객체를 돌려줍니다.
3. 학생 한 명의 정보입니다. 무엇이 찍힐까요? 보기에서 고르세요.
const student = {
id: 7,
name: '민지',
score: 90,
};
const { id, ...info } = student;
info.score = 95;
console.log(student.score);
- ①
90 - ②
95 - ③
undefined
힌트
6번 줄의 info가 student와 같은 객체인지, 새 객체인지 먼저 정합니다.
정답
90①입니다. 6번 줄의 info는 남은 name과 score를 담은 새 객체입니다. 7번 줄은 그 새 객체의 score 줄을 95로 바꿀 뿐이고, student의 score 줄은 90 그대로입니다.
- ② info가 student와 같은 객체를 가리킨다고 봤습니다.
const info = student;라면 95가 찍힙니다. - ③ 남은 속성이 info로 옮겨 가 student에서 빠진다고 봤습니다. 구조 분해로 꺼내도 원래 객체는 그대로입니다.
컴퓨터에서 해 보기(선택) — 점수에 들지 않습니다. 폰으로만 보고 있다면 건너뛰세요.
2절 첫 코드를 main.js에 저장해 돌린 뒤, 6번 줄의 ...rest를 name 앞으로 옮깁니다. 무엇이 찍히고 어디서 멈출지 먼저 적고, 바꿔 돌려서 맞춰 봅니다. 아무것도 찍지 않고 SyntaxError로 멈추지 않으면 2절 끝을 다시 봅니다.
저장하고 돌리는 법은 내 컴퓨터에서 돌려 보기에 있습니다.