구조 분해 할당

구조 분해 할당 구문은 배열이나 객체의 속성을 해체하여 그 값을 개별 변수에 담을 수 있게 하는 JavaScript 표현식입니다.

구문

var a, b, rest;
[a, b] = [10, 20];
console.log(a); // 10
console.log(b); // 20

[a, b, ...rest] = [10, 20, 30, 40, 50];
console.log(a); // 10
console.log(b); // 20
console.log(rest); // [30, 40, 50]

({ a, b } = { a: 10, b: 20 });
console.log(a); // 10
console.log(b); // 20


// Stage 4(finished) proposal
({a, b, ...rest} = {a: 10, b: 20, c: 30, d: 40});
console.log(a); // 10
console.log(b); // 20
console.log(rest); // {c: 30, d: 40}

설명

객체 및 배열 리터럴 표현식을 사용하면 즉석에서 쉽게 데이터 뭉치를 만들 수 있습니다.

var x = [1, 2, 3, 4, 5];

구조 분해 할당의 구문은 위와 비슷하지만, 대신 할당문의 좌변에서 사용하여, 원래 변수에서 어떤 값을 분해해 할당할지 정의합니다.

var x = [1, 2, 3, 4, 5];
var [y, z] = x;
console.log(y); // 1
console.log(z); // 2

구조 분해 할당은 Perl이나 Python 등 다른 언어가 가지고 있는 기능입니다.

배열 구조 분해

기본 변수 할당

var foo = ["one", "two", "three"];

var [one, two, three] = foo;
console.log(one); // "one"
console.log(two); // "two"
console.log(three); // "three"

선언에서 분리한 할당

변수의 선언이 분리되어도 구조 분해를 통해 값을 할당할 수 있습니다.

var a, b;

[a, b] = [1, 2];
console.log(a); // 1
console.log(b); // 2

기본값

변수에 기본값을 할당하면, 분해한 값이 undefined일 때 그 값을 대신 사용합니다.

var a, b;

[a=5, b=7] = [1];
console.log(a); // 1
console.log(b); // 7

변수 값 교환하기

하나의 구조 분해 표현식만으로 두 변수의 값을 교환할 수 있습니다.

구조 분해 할당 없이 두 값을 교환하려면 임시 변수가 필요합니다. (일부 로우 레벨 언어에서는 XOR 교체 트릭을 사용할 수 있습니다)

var a = 1;
var b = 3;

[a, b] = [b, a];
console.log(a); // 3
console.log(b); // 1

함수가 반환한 배열 분석

함수는 이전부터 배열을 반환할 수 있었습니다. 구조 분해를 사용하면 반환된 배열에 대한 작업을 더 간결하게 수행할 수 있습니다.

아래 예제에서 f()는 출력으로 배열 [1, 2]을 반환하는데, 하나의 구조 분해만으로 값을 분석할 수 있습니다.

function f() {
  return [1, 2];
}

var a, b;
[a, b] = f();
console.log(a); // 1
console.log(b); // 2

일부 반환 값 무시하기

다음과 같이 필요하지 않은 반환 값을 무시할 수 있습니다.

function f() {
  return [1, 2, 3];
}

var [a, , b] = f();
console.log(a); // 1
console.log(b); // 3

반환 값을 모두 무시할 수도 있습니다.

[,,] = f();

변수에 배열의 나머지를 할당하기

배열을 구조 분해할 경우, 나머지 구문을 이용해 분해하고 남은 부분을 하나의 변수에 할당할 수 있습니다.

var [a, ...b] = [1, 2, 3];
console.log(a); // 1
console.log(b); // [2, 3]

나머지 요소의 오른쪽 뒤에 쉼표가 있으면 SyntaxError가 발생합니다.

var [a, ...b,] = [1, 2, 3];
// SyntaxError: rest element may not have a trailing comma

정규 표현식과 일치하는 값 해체하기

정규 표현식의 exec() 메서드는 일치하는 부분를 찾으면 그 문자열에서 정규식과 일치하는 부분 전체를 배열의 맨 앞에, 그리고 그 뒤에 정규식에서 괄호로 묶인 각 그룹과 일치하는 부분을 포함하는 배열을 반환합니다. 구조 분해 할당은 필요하지 않은 경우 일치하는 전체 부분은 무시하고 필요한 부분만 쉽게 빼올 수 있습니다.

function parseProtocol(url) { 
  var parsedURL = /^(\w+)\:\/\/([^\/]+)\/(.*)$/.exec(url);
  if (!parsedURL) {
    return false;
  }
  console.log(parsedURL); // ["https://developer.mozilla.org/en-US/Web/JavaScript", "https", "developer.mozilla.org", "en-US/Web/JavaScript"]

  var [, protocol, fullhost, fullpath] = parsedURL;
  return protocol;
}

console.log(parseProtocol('https://developer.mozilla.org/en-US/Web/JavaScript')); // "https"

객체 구조 분해

기본 할당

var o = {p: 42, q: true};
var {p, q} = o;

console.log(p); // 42
console.log(q); // true

선언 없는 할당

구조 분해를 통해 변수의 선언과 분리하여 변수에 값을 할당할 수 있습니다.

var a, b;

({a, b} = {a: 1, b: 2});

참고: 할당 문을 둘러싼 ( .. )는 선언 없이 객체 리터럴(object literal) 비구조화 할당을 사용할 때 필요한 구문입니다.

{a, b} = {a:1, b:2}는 유효한 독립 구문이 아닙니다. 좌변의 {a, b}이 객체 리터럴이 아닌 블록으로 간주되기 때문입니다.

하지만, ({a, b} = {a:1, b:2})는 유효한데, var {a, b} = {a:1, b:2}와 같습니다.

( .. ) 표현식 앞에는 세미콜론이 있어야 합니다. 그렇지 않을 경우 이전 줄과 연결되어 함수를 실행하는데 이용될 수 있습니다.

새로운 변수 이름으로 할당하기

객체로부터 속성을 해체하여 객체의 원래 속성명과는 다른 이름의 변수에 할당할 수 있습니다.

var o = {p: 42, q: true};
var {p: foo, q: bar} = o;

console.log(foo); // 42
console.log(bar); // true

기본값

객체로부터 해체된 값이 undefined인 경우, 변수에 기본값을 할당할 수 있습니다.

var {a = 10, b = 5} = {a: 3};

console.log(a); // 3
console.log(b); // 5

기본값 갖는 새로운 이름의 변수에 할당하기

새로운 변수명 할당과 기본값 할당을 한번에 할 수 있습니다.

var {a: aa = 10, b: bb = 5} = {a: 3};

console.log(aa); // 3
console.log(bb); // 5

함수 매개변수의 기본값 설정하기

ES5 버전

function drawES5Chart(options) {
  options = options === undefined ? {} : options;
  var size = options.size === undefined ? 'big' : options.size;
  var cords = options.cords === undefined ? { x: 0, y: 0 } : options.cords;
  var radius = options.radius === undefined ? 25 : options.radius;
  console.log(size, cords, radius);
  // 이제 드디어 차트 그리기 수행
}

drawES5Chart({
  cords: { x: 18, y: 30 },
  radius: 30
});

ES2015 버전

function drawES2015Chart({size = 'big', cords = { x: 0, y: 0 }, radius = 25} = {}) {
  console.log(size, cords, radius);
  // 차트 그리기 수행
}

drawES2015Chart({
  cords: { x: 18, y: 30 },
  radius: 30
});

위의 drawES2015Chart 함수의 원형에서 구조 분해된 좌변에 빈 오브젝트 리터럴을 할당하는 것을 볼 수 있습니다 {size = 'big', cords = {x: 0, y: 0}, radius = 25} = {}. 빈 오브젝트를 우변에 할당하지 않고도 함수를 작성할 수 있습니다. 하지만, 지금의 형태에서는 단순히 drawES2015Chart()와 같이 어떤 매개변수 없이도 호출할 수 있지만, 우변의 빈 오브젝트 할당을 없앤다면 함수 호출시 적어도 하나의 인자가 제공되어야만 합니다. 이 함수가 어떠한 매개변수 없이도 호출할 수 있길 원한다면 지금 형태가 유용하고, 무조건 객체를 넘기길 원하는 경우에는 빈 객체 할당을 하지 않는 것이 유용할 수 있습니다.

중첩된 객체 및 배열의 구조 분해

var metadata = {
    title: "Scratchpad",
    translations: [
       {
        locale: "de",
        localization_tags: [ ],
        last_edit: "2014-04-14T08:43:37",
        url: "/de/docs/Tools/Scratchpad",
        title: "JavaScript-Umgebung"
       }
    ],
    url: "/en-US/docs/Tools/Scratchpad"
};

var { title: englishTitle, translations: [{ title: localeTitle }] } = metadata;

console.log(englishTitle); // "Scratchpad"
console.log(localeTitle);  // "JavaScript-Umgebung"

for of 반복문과 구조 분해

var people = [
  {
    name: "Mike Smith",
    family: {
      mother: "Jane Smith",
      father: "Harry Smith",
      sister: "Samantha Smith"
    },
    age: 35
  },
  {
    name: "Tom Jones",
    family: {
      mother: "Norah Jones",
      father: "Richard Jones",
      brother: "Howard Jones"
    },
    age: 25
  }
];

for (var {name: n, family: { father: f } } of people) {
  console.log("Name: " + n + ", Father: " + f);
}

// "Name: Mike Smith, Father: Harry Smith"
// "Name: Tom Jones, Father: Richard Jones"

함수 매개변수로 전달된 객체에서 필드 해체하기

function userId({id}) {
  return id;
}

function whois({displayName: displayName, fullName: {firstName: name}}){
  console.log(displayName + " is " + name);
}

var user = {
  id: 42,
  displayName: "jdoe",
  fullName: {
      firstName: "John",
      lastName: "Doe"
  }
};

console.log("userId: " + userId(user)); // "userId: 42"
whois(user); // "jdoe is John"

이 예제는 user 객체로부터 id, displayNamefirstName 을 해체해 출력합니다.

계산된 속성 이름과 구조 분해

계산된 속성 이름(computed property name)은, 객체 리터럴과 비슷하게 구조 분해에도 사용될 수 있습니다.

let key = "z";
let { [key]: foo } = { z: "bar" };

console.log(foo); // "bar"

객체 구조 분해에서 Rest

Rest/Spread Properties for ECMAScript 제안(stage 3)에서는 구조 분해에 rest 구문을 추가하고 있습니다. rest 속성은 구조 분해 패턴으로 걸러지지 않은 열거형 속성의 키를 가진 나머지 항목들을 모읍니다.

let {a, b, ...rest} = {a: 10, b: 20, c: 30, d: 40}
a; // 10 
b; // 20 
rest; // { c: 30, d: 40 }

속성 이름이 유효한 JavaScript 식별자명이 아닌 경우

구조 분해는 JavaScript 식별자 이름으로 적합하지 않은 속성명이 제공된 경우에도 이용할 수 있습니다. 이 때는 대체할 유효한 식별자명을 제공해야 합니다.

const foo = { 'fizz-buzz': true };
const { 'fizz-buzz': fizzBuzz } = foo;

console.log(fizzBuzz); // "true"

명세

Specification Status Comment
ECMAScript 2015 (6th Edition, ECMA-262)
The definition of 'Destructuring assignment' in that specification.
Standard Initial definition.
ECMAScript Latest Draft (ECMA-262)
The definition of 'Destructuring assignment' in that specification.
Draft  

브라우저 호환성

Update compatibility data on GitHub
DesktopMobileServer
ChromeEdgeFirefoxInternet ExplorerOperaSafariAndroid webviewChrome for AndroidFirefox for AndroidOpera for AndroidSafari on iOSSamsung InternetNode.js
Destructuring assignmentChrome Full support 49Edge Full support 14Firefox Full support 41
Notes
Full support 41
Notes
Notes Firefox provided a non-standard destructuring implementation from Firefox 2 to 40.
IE No support NoOpera Full support YesSafari Full support 8WebView Android Full support 49Chrome Android Full support 49Firefox Android Full support 41
Notes
Full support 41
Notes
Notes Firefox provided a non-standard destructuring implementation from Firefox 2 to 40.
Opera Android Full support YesSafari iOS Full support 8Samsung Internet Android Full support 5.0nodejs Full support 6.0.0
Computed property namesChrome Full support 49Edge Full support 14Firefox Full support 41IE No support NoOpera Full support YesSafari Full support 10WebView Android Full support 49Chrome Android Full support 49Firefox Android Full support 41Opera Android Full support YesSafari iOS Full support 10Samsung Internet Android Full support 5.0nodejs Full support Yes
Rest in arraysChrome Full support 49Edge Full support 14
Disabled
Full support 14
Disabled
Disabled From version 14: this feature is behind the Enable experimental Javascript features preference.
Firefox Full support 41IE No support NoOpera Full support YesSafari No support NoWebView Android Full support 49Chrome Android Full support 49Firefox Android Full support 41Opera Android Full support YesSafari iOS No support NoSamsung Internet Android Full support 5.0nodejs Full support 6.0.0
Rest in objects
Experimental
Chrome Full support 60Edge No support NoFirefox Full support 55IE No support NoOpera Full support YesSafari Full support 11.1WebView Android Full support 60Chrome Android Full support 60Firefox Android Full support 55Opera Android Full support YesSafari iOS Full support 11.3Samsung Internet Android Full support 8.0nodejs Full support 8.3.0

Legend

Full support  
Full support
No support  
No support
Experimental. Expect behavior to change in the future.
Experimental. Expect behavior to change in the future.
See implementation notes.
See implementation notes.
User must explicitly enable this feature.
User must explicitly enable this feature.

같이 보기