7장 디스트럭처링

2023.06.16

7.1 개요

  • 디스트럭처링은 그것의 구조에서 무언가를 추출할 때 사용하는 문법이다.
  • ES2018에서 확장된 디스트럭처링 구문은 더 간결하고 매우 강력하여 기본값, 이름 변경, 중첩 및 나머지 구문을 제공한다.
  • 특히 함수 매개변수에 적용될 때 명확성과 표현력을 제공한다.

7.2 기본 객체 디스트럭처링

1let obj = { fisrt: 1, second: 2 };
2let a = obj.a; // 오래된 수동 디스트럭처링
3let { fisrt: b } = obj; // 새로운 디스트럭처링 구문
4
5console.log(a); // 1;
6console.log(b); // 1;
  • 위 디스럭처링은 자바스크립트 엔진에 first 속성의 값을 변수 a와 b에 넣도록 지시한다.
  • 객체 리터럴과 디스트럭처링 구문은 그 목적이 반대이지만 정확히 같은 모양이다.
  • 자바스크립트 엔진은 문맥으로 객체 리터럴을 작성하는지 아니면 객체 디스트럭처링 패턴을 작성하는지 알고 있다.
1let { first: a } = { first: 42 };
  • 위의 코드에서 대상은 let, 변수 a로 선언되는 변수이다.
  • 디스트럭처링 패턴의 대상은 변수, 객체 속성, 배열 엔트리 등 할당할 수 있는 모든 것이 될 수 있다.
  • 수동 디스트럭처링과 마찬가지로 객체에 읽으려는 속성이 없는 경우 변수는 undefined를 가져온다.
1let obj = { first: 2, second: 3 };
2let { third: c } = obj;
3console.log(c); // undefined
  • 디스트럭처링시 콜론과 이름은 생략할 수 있다.
1let obj = { first: 2, second: 3 };
2let { first } = obj;
3console.log(first); // 2
  • 하나 이상의 속성을 선택할 때 디스트럭처링은 강력해지기 시작한다.
1let obj = { first: 2, second: 3 };
2let { first, second } = obj;
3
4// 위 코드는 다음 코드와 같다.
5let obj = { first: 2, second: 3 };
6let first = obj.first,
7  second = obj.second;
  • 디스트럭처링은 변수/상수 초기화에만 국한되지 않는다. 모든 할당에서 사용할 수 있다.
1let first, second;
2
3{first, second} = getSomeObject(); // 문법 오류
4({first, second} = getSomeObject()); // 동작함
  • 자바스크립트 파서가 명령문을 예상하는 할당을 수행하는 경우 파서가 초기 중괄호를 블록의 시작으로 처리하므로 할당 표현식을 괄호로 묶어야 한다.
  • 디스트럭처링은 이전 스타일과 동등한 코드로 속성을 선택하기 위한 문법적 설탕(syntactic sugar)일 뿐이다. 문법적 설탕이 없는 버전이 어떻게 생겼는지 보면 도움이 된다.
1let { first, second } = 42;
2// first와 second 모두 undefined
3
4const temp = 42;
5let first = temp.first,
6  second = temp.second;
7// first와 second 모두 undefined
  • 숫자를 객체처럼 취급할 떄와 마찬가지로 기본 숫자는 Number로 강제 변환되며, 이 예에서 first, second 속성은 해당 객체에서 읽게 된다. 물론 존재하지 않으므로 undefined 값을 얻는다.

7.3 기본 배열(과 이터러블) 디스트럭처링

  • 배열과 다른 이터러블도 디스트럭처링할 수 있다.
1const arr = [1, 2];
2const [first, second] = arr;
3console.log(first, second); // 1, 2
4
5// 위 코드는 다음 코드와 같다.
6const arr = [1, 2];
7const first = arr[0],
8  second = arr[1];
9console.log(first, second); // 1, 2
  • 객체 디스트럭처링과 달리, 명령문이 예상되는 위치에 초기화하는 대신 할당을 수행할 때 표현식을 괄호로 묶을 필요가 없다. 초기 대괄호 [ 는 초기 중괄호 { 와 같이 모호하지 않다.
1const arr = [1, 2];
2let first, second;
3[first, second] = arr;
4
5console.log(first, second); // 1, 2

7.4 기본값

  • 수동 디스트럭처링과 달리 속성이 없거나 값이 undefined인 경우에만 적용되는 기본 값을 지정할 수 있다.
1const obj = { first: 1, second: 2 };
2const { third = 3 } = obj;
3
4console.log(third); // 3
5
6const obj2 = { fourth: 0 };
7const { fourth = 3 } = obj2;
8
9console.log(fourth); // 0, 3이 아님
  • 기본값은 유횻값이 거짓으로 평가되는 모든 값이 아니라 undefined인 경우에만 사용된다.
1const getDefault = (val) => val;
2const obj = { first: 1, second: undefined };
3const { first = getDefault('it is 1'), second = getDefault('it is 2'), third = getDefault(second) } = obj;
4
5console.log(first); // 1
6console.log(second); // it is 2
7console.log(third); // it is 2
  • 디스트럭처링은 소스 코드 순서대로 수행된다.
  • 소스 코드 순서로 수행되기 때문에 이후 대상은 기본값에서 이전 대상의 값을 참조할 수 있다.

7.5 디스트럭처링 패턴에서 나머지 문법

  • 디스트럭처링할 때 나머지 문법을 사용할 수 있고, 패턴의 나머지 엔트리는 끝에 있어야 한다.
1const a = [1, 2, 3, 4];
2const [first, second, ...rest] = a;
3
4console.log(first); // 1
5console.log(second); // 2
6console.log(rest); // [3, 4]

7.6 다른 이름 사용하기

  • 단축 문법을 사용하는 대신 명시적 변수 이름을 포함하면 디스트럭처링을 통해서도 다른 이름을 사용할 수도 있다.
1const obj = { 'my-name': 1 };
2const { 'my-name': myName } = obj;
3console.log(myName); // 1
  • 배열은 객체이기 때문에 배열 디스트럭처링보다는 객체 디스트럭처링을 사용하여 더 명확하게 할 수 있다.
1const arr = [1, 2, 3, 4, 5];
2const [first, , , last] = arr; // 배열 디스트럭처링
3const { 0: a, 4: e } = arr; // 객체 디스트럭처링
4
5console.log(first, last); // 1, 5
6console.log(a, e); // 1, 5
  • 위의 인덱스 트릭은 배열 인덱스가 속성 이름이기 때문에 작동한다. 따라서, 이터러블에서 작동하지 않고 배열에서만 작동한다. 이터러블이 유한한 경우 Array.from을 사용하여 이 트릭을 적용할 수 있다.

7.7 계산된 속성 이름

  • 객체 디스트럭처링은 객체 리터럴과 똑같은 문법을 사용하므로 디스터럭처링할 때도 계산된 속성 이름을 사용할 수 있다.
1let source = { a: 'ayy', b: 'bee' };
2let name = Math.random() < 0.5 ? 'a' : 'b';
3let { [name]: dest } = source;
4
5console.log(dest); // 반은 "ayy", 반은 "bee";

7.8 중첩된 디스트럭처링

  • 패턴에서 중첩을 사용하면 디스트럭처링 구문이 더 깊어질 수 있다.
1const obj = { a: [1, 2, 3], b: [4, 5, 6] };
2let {
3  a: [first, second],
4} = obj;
5
6console.log(first, second); // 1, 2
  • 객체와 배열 리터럴과 마찬가지로 사용할 수 있는 중첩 수는 기본적으로 무제한이다.

7.9 매개변수 디스트럭처링

  • 디스트럭처링은 할당만을 위한 것이 아니다. 함수 매개변수에도 디스트럭처링할 수 있다.
  • 디스트럭처링되지 않는 매개변수가 첫 번째이거나 유일한 매개변수일 필요는 없다. 매개변수 목록 어디에나 있을 수 있다.
1function example(first, { a, b }, last) {
2  console.log(first, a, b, last);
3}
4const o = { a: 'ayy', b: 'bee', c: 'see', d: 'dee' };
5example('alpha', o, 'omega'); // "alpha" "ayy" "bee" "omega"
6example('primero', { a: 1, b: 2 }, 'ultimo'); // "primero" 1 2 "ultimo"
  • 기본 함수 매개변수는 사실상 기본 디스트럭처링 값일 뿐이다. 함수 매개변수 목록은 매개변수 없이 예를 호출할 때 다음과 같은 디스트럭처링과 동일하다.
1function example(first, { a, b }, last) {
2  console.log(first, a, b, last);
3}
4const o = { a: 'ayy', b: 'bee', c: 'see', d: 'dee' };
5example('alpha', o, 'omega'); // "alpha" "ayy" "bee" "omega"
6example('primero', { a: 1, b: 2 }, 'ultimo'); // "primero" 1 2 "ultimo"

7.10 반복문에서 디스트럭처링

  • for-in과 for-of 루프는 루프 반복이 시작될 때 루프변수에 할당한다. 여기에도 디스트럭처링을 사용할 수 있다.
1const arr = [
2  { name: 'one', value: 1 },
3  { name: 'two', value: 2 },
4  { name: 'forty-two', value: 42 },
5];
6for (const { name, value } of arr) {
7  console.log('Name: ' + name + ', value: ' + value);
8}
9
10const obj = { a: 1, b: 2, c: 3 };
11for (const name in obj) {
12  if (obj.hasOwnProperty(name)) {
13    const value = obj[name];
14    console.log(name + ' = ' + value);
15  }
16}
  • 또한, 이터러블 디스트럭처링으로 코드를 훨씬 더 명확하게 할 수 있다.
1const obj = { a: 1, b: 2, c: 3 };
2for (const [name, value] of Object.entries(obj)) {
3  console.log(name + ' = ' + value);
4}

7.11 과거 습관을 새롭게

7.11.1 객체에서 일부 속성만 가져올 때 디스트럭처링 사용

  • 추후에 객체 자체가 필요하지 않다면 디스트럭처링을 사용하자.
1const { firstName, lastName } = getThePerson();
2console.log(firstName, lastName);

7.11.2 옵션 객체에 디스트럭처링 사용

  • 디스트럭처링 사용을 고려하자.