分割代入

分割代入 (Destructuring assignment) 構文は、配列から値を取り出して、あるいはオブジェクトからプロパティを取り出して別個の変数に代入することを可能にする JavaScript の式です。

構文

let 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}

解説

オブジェクトリテラルと配列リテラルは、いくつかのデータをアドホックにまとめる簡単な方法を提供します。

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

分割代入は似たような構文を使用しますが、代入の左辺が元の変数からどの値を受け取るかを定義します。

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

この機能は、Perl や Python などの言語に存在する機能に似ています。

配列の分割代入

簡単な例

const foo = ['one', 'two', 'three'];

const [red, yellow, green] = foo;
console.log(red); // "one"
console.log(yellow); // "two"
console.log(green); // "three"

宣言後の割り当て

変数は宣言とは別に、分割代入によって値を代入することができます。

let a, b;

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

既定値

配列から取り出した値が undefined だった場合に使用される既定値を指定できます。

let a, b;

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

変数の入れ替え

分割代入を使用して、複数の変数の値を入れ替えることができます。

分割代入を使用せずに 2 つの値を交換するには、一時変数 (または、一部の低水準言語においては XOR 交換アルゴリズム) が必要です。

let a = 1;
let b = 3;

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

const arr = [1,2,3];
[arr[2], arr[1]] = [arr[1], arr[2]];
console.log(arr); // [1,3,2]

関数から返された配列の解析

関数は配列を返すことができます。分割代入によって、返された配列の使用をより簡潔に記述できます。

この例では、f() は出力として値 [1, 2] を返しており、分割代入により 1行で解析できます。

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

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

返値の無視

関心のない返値は無視することができます。

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

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

const [c] = f();
console.log(c); // 1

このようにすべての返値を無視することもできます。

[,,] = f();

配列の残余部分への変数の代入

配列を分割するときに残余パターンを使用して、配列の残りの部分を取り出して変数に代入できます。

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

左辺側で残余要素とともに末尾のカンマが使用されていると、SyntaxError が発生しますので注意してください。

const [a, ...b,] = [1, 2, 3];

// SyntaxError: rest 要素の末尾にカンマがあってはなりません
// 常に最後の要素として rest 演算子を使用してください。

正規表現の一致からの値取得

正規表現オブジェクトの exec() メソッドは一致するものを見つけ、最初に一致した文字列全体の一部と、正規表現内の各括弧で囲まれたグループに一致した文字列の部分を含む配列を返します。分割代入によって、簡単にこの配列の一部分を取り出せます。また必要でない場合は、完全一致を無視できます。

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

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

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

オブジェクトの分割代入

簡単な例

const user = {
    id: 42,
    is_verified: true
};

const {id, is_verified} = user;

console.log(id); // 42
console.log(is_verified); // true 

宣言のない代入

分割代入は代入文で宣言することなく行うことができます。

let a, b;

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

メモ: 代入文の周りの ( ... ) は宣言のないオブジェクトリテラル分割代入を使用するときに必要な構文です。

{a, b} = {a: 1, b: 2} は有効なスタンドアロンの構文ではありません。というのも、左辺の {a, b} はブロックでありオブジェクトリテラルではないと考えられるからです。

ですが、({a, b} = {a: 1, b: 2}) 形式は有効です。var {a, b} = {a: 1, b: 2} と考えられるためです。

( ... ) の式の前にセミコロンが必要です。そうしなければ、前の行の関数を実行に使用される可能性があります。

異なる名前を持つ変数への代入

オブジェクトから変数を取り出して、オブジェクトのプロパティとは異なる名前の変数に代入することができます。

const o = {p: 42, q: true};
const {p: foo, q: bar} = o;
 
console.log(foo); // 42 
console.log(bar); // true

ここで、例えば、const {p: foo} = o はオブジェクト o から p という名前のプロパティを取り、foo という名前のローカル変数へ代入します。

既定値

オブジェクトから取り出した値が undefined であるときの既定値を、変数に割り当てることができます。

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

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

新しい変数名の割り当てとデフォルト値の提供

両方ともプロパティにすることができます

  • オブジェクトから取り出して異なる名前の変数に代入します。
  • 取り出した値が undefined である場合に備えて、デフォルト値を割り当てます。
const {a: aa = 10, b: bb = 5} = {a: 3};

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

引数に指定されたオブジェクトの属性への参照

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

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

function whois({displayName, fullName: {firstName: name}}) {
  return `${displayName} is ${name}`;
}

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

上記では id, displayName, firstName をオブジェクトから取得し、出力します。

関数の引数に対する既定値の設定

function drawChart({size = 'big', coords = {x: 0, y: 0}, radius = 25} = {}) {
  console.log(size, coords, radius);
  // グラフの描画
}

drawChart({
  coords: {x: 18, y: 30},
  radius: 30
});

上記の drawChart の関数シグネチャの中で、{size = 'big', coords = {x: 0, y: 0}, radius = 25} = {} として、分割代入の左辺に、右辺側で空のオブジェクトリテラルを代入しています。右辺の代入がない関数を記入することもできます。しかし、右辺の代入を取り除いた場合、関数は実行されたときに少なくともひとつの引数が提供されることを期待しますが、この形式では何も引数を指定せずに単純に drawChart() を呼び出すことができます。この設計は引数を指定せずに関数を呼び出せるようにしたい場合に役に立ちますし、もう一方の形式は、オブジェクトを確実に関数に渡したい場合に役に立ちます。

入れ子になったオブジェクトと配列の分割代入

const 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'
};

let {
  title: englishTitle, // rename
  translations: [
    {
       title: localeTitle, // rename
    },
  ],
} = metadata;

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

イテレーターでの分割代入の利用

const 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 (const {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"

計算されたオブジェクトのプロパティの名前と分割代入

オブジェクトリテラルのような計算されたプロパティの名前も分割代入で使用できます。

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

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

オブジェクトの分割代入の残り

Rest/Spread Properties for ECMAScript 提案 (ステージ 4) は、分割代入に rest 構文を追加しています。残余プロパティは、分割パターンによってすでに取り出されていない、残りの列挙可能なプロパティのキーを収集します。

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

無効な JavaScript 識別子をプロパティ名として使用する

JavaScript で有効な代替識別子を与えることにより、JavaScript で有効ではない識別子であるプロパティ名を分割代入で使用できます。

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

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

配列とオブジェクトの分割代入の組み合わせ

配列とオブジェクトの分割代入は組み合わせることができます。配列 props の 3 番目の要素にあるオブジェクトの name プロパティが欲しい場合、次の操作ができます。

const props = [
  { id: 1, name: 'Fizz'},
  { id: 2, name: 'Buzz'},
  { id: 3, name: 'FizzBuzz'}
];

const [,, { name }] = props;

console.log(name); // "FizzBuzz"

オブジェクトが分割されるときにはプロトタイプチェーンが参照される

オブジェクトが分割されるときで、自分自身のプロパティがアクセスされない場合は、プロトタイプチェーンを辿って参照が続けられます。

let obj = {self: '123'};
obj.__proto__.prot = '456';
const {self, prot} = obj;
// self "123"
// prot "456"(プロトタイプチェーンへのアクセス)

仕様

仕様書
ECMAScript (ECMA-262)
Destructuring assignment の定義

ブラウザーの互換性

Update compatibility data on GitHub
デスクトップモバイルサーバー
ChromeEdgeFirefoxInternet ExplorerOperaSafariAndroid webviewAndroid 版 ChromeAndroid 版 FirefoxAndroid 版 OperaiOSのSafariSamsung InternetNode.js
Destructuring assignmentChrome 完全対応 49Edge 完全対応 14Firefox 完全対応 41
補足
完全対応 41
補足
補足 Firefox provided a non-standard destructuring implementation from Firefox 2 to 40.
IE 未対応 なしOpera 完全対応 36Safari 完全対応 8WebView Android 完全対応 49Chrome Android 完全対応 49Firefox Android 完全対応 41
補足
完全対応 41
補足
補足 Firefox provided a non-standard destructuring implementation from Firefox 2 to 40.
Opera Android 完全対応 36Safari iOS 完全対応 8Samsung Internet Android 完全対応 5.0nodejs 完全対応 6.0.0
Computed property namesChrome 完全対応 49Edge 完全対応 14Firefox 完全対応 41IE 未対応 なしOpera 完全対応 36Safari 完全対応 10WebView Android 完全対応 49Chrome Android 完全対応 49Firefox Android 完全対応 41Opera Android 完全対応 36Safari iOS 完全対応 10Samsung Internet Android 完全対応 5.0nodejs 完全対応 6.0.0
Rest in arraysChrome 完全対応 49Edge 完全対応 16
完全対応 16
完全対応 14
無効
無効 From version 14: this feature is behind the Enable experimental Javascript features preference.
Firefox 完全対応 41IE 未対応 なしOpera 完全対応 36Safari 完全対応 9.1WebView Android 完全対応 49Chrome Android 完全対応 49Firefox Android 完全対応 41Opera Android 完全対応 36Safari iOS 完全対応 9.3Samsung Internet Android 完全対応 5.0nodejs 完全対応 6.0.0
Rest in objects
実験的
Chrome 完全対応 60Edge 完全対応 79Firefox 完全対応 55IE 未対応 なしOpera 完全対応 47Safari 完全対応 11.1WebView Android 完全対応 60Chrome Android 完全対応 60Firefox Android 完全対応 55Opera Android 完全対応 44Safari iOS 完全対応 11.3Samsung Internet Android 完全対応 8.0nodejs 完全対応 8.3.0

凡例

完全対応  
完全対応
未対応  
未対応
実験的。動作が変更される可能性があります。
実験的。動作が変更される可能性があります。
実装ノートを参照してください。
実装ノートを参照してください。
ユーザーが明示的にこの機能を有効にしなければなりません。
ユーザーが明示的にこの機能を有効にしなければなりません。

関連情報