JavaScriptで最も使用される2つのデータ構造は、Object
とArray
です。
- オブジェクトを使用すると、キーでデータ項目を格納する単一のエンティティを作成できます。
- 配列を使用すると、データ項目を順序付けられたリストにまとめることができます。
ただし、これらを関数に渡す場合、すべてが必要とは限りません。関数は特定の要素またはプロパティのみを必要とする場合があります。
デストラクチャリング代入は、配列またはオブジェクトを複数の変数に「展開」できる特別な構文です。場合によっては、これがより便利であるためです。
デストラクチャリングは、多くのパラメーター、デフォルト値などを含む複雑な関数でもうまく機能します。すぐにそれを見てみましょう。
配列のデストラクチャリング
配列を変数にデストラクチャリングする例を次に示します。
// we have an array with a name and surname
let arr = ["John", "Smith"]
// destructuring assignment
// sets firstName = arr[0]
// and surname = arr[1]
let [firstName, surname] = arr;
alert(firstName); // John
alert(surname); // Smith
これで、配列のメンバーではなく、変数を使用できます。
split
やその他の配列を返すメソッドと組み合わせると、非常に効果的です。
let [firstName, surname] = "John Smith".split(' ');
alert(firstName); // John
alert(surname); // Smith
ご覧のとおり、構文はシンプルです。ただし、いくつかの独特な詳細があります。より多くの例を見て、より深く理解しましょう。
項目を変数にコピーすることによって「デストラクチャリング」するため、「デストラクチャリング代入」と呼ばれます。ただし、配列自体は変更されません。
これは単に、次のように記述するより短い方法です。
// let [firstName, surname] = arr;
let firstName = arr[0];
let surname = arr[1];
配列の不要な要素は、余分なカンマを使用して削除することもできます。
// second element is not needed
let [firstName, , title] = ["Julius", "Caesar", "Consul", "of the Roman Republic"];
alert( title ); // Consul
上記のコードでは、配列の2番目の要素はスキップされ、3番目の要素はtitle
に割り当てられ、残りの配列要素もスキップされます(それらに対応する変数がないため)。
実際、配列だけでなく、任意の反復可能オブジェクトで使用できます。
let [a, b, c] = "abc"; // ["a", "b", "c"]
let [one, two, three] = new Set([1, 2, 3]);
これは、内部的にデストラクチャリング代入が右辺の値を反復処理することによって機能するためです。これは、=
の右側の値に対してfor..of
を呼び出し、値を割り当てるための構文シュガーの一種です。
左辺には、任意の「代入可能」なものを使用できます。
たとえば、オブジェクトのプロパティです。
let user = {};
[user.name, user.surname] = "John Smith".split(' ');
alert(user.name); // John
alert(user.surname); // Smith
前の章では、Object.entries(obj)メソッドについて説明しました。
デストラクチャリングと組み合わせて、オブジェクトのキーと値をループ処理できます。
let user = {
name: "John",
age: 30
};
// loop over the keys-and-values
for (let [key, value] of Object.entries(user)) {
alert(`${key}:${value}`); // name:John, then age:30
}
Map
の場合、反復可能であるため、同様のコードはよりシンプルです。
let user = new Map();
user.set("name", "John");
user.set("age", "30");
// Map iterates as [key, value] pairs, very convenient for destructuring
for (let [key, value] of user) {
alert(`${key}:${value}`); // name:John, then age:30
}
デストラクチャリング代入を使用して2つの変数の値を入れ替えるためのよく知られたトリックがあります。
let guest = "Jane";
let admin = "Pete";
// Let's swap the values: make guest=Pete, admin=Jane
[guest, admin] = [admin, guest];
alert(`${guest} ${admin}`); // Pete Jane (successfully swapped!)
ここでは、2つの変数のテンポラリ配列を作成し、それをすぐに入れ替えた順序でデストラクチャリングします。
これにより、2つ以上の変数を入れ替えることができます。
rest ‘…’
通常、配列が左側のリストよりも長い場合、「余分な」アイテムは省略されます。
たとえば、ここでは2つのアイテムのみが取得され、残りは無視されます。
let [name1, name2] = ["Julius", "Caesar", "Consul", "of the Roman Republic"];
alert(name1); // Julius
alert(name2); // Caesar
// Further items aren't assigned anywhere
それに続くものをすべて収集する場合、3つのドット"..."
を使用して「残りの部分」を取得するパラメーターをもう1つ追加できます。
let [name1, name2, ...rest] = ["Julius", "Caesar", "Consul", "of the Roman Republic"];
// rest is an array of items, starting from the 3rd one
alert(rest[0]); // Consul
alert(rest[1]); // of the Roman Republic
alert(rest.length); // 2
rest
の値は、残りの配列要素の配列です。
rest
の代わりに他の変数名を使用できます。3つのドットの前に配置し、デストラクチャリング代入の最後に配置するようにしてください。
let [name1, name2, ...titles] = ["Julius", "Caesar", "Consul", "of the Roman Republic"];
// now titles = ["Consul", "of the Roman Republic"]
デフォルト値
配列が左側の変数のリストよりも短い場合、エラーは発生しません。存在しない値はundefinedと見なされます。
let [firstName, surname] = [];
alert(firstName); // undefined
alert(surname); // undefined
不足している値を置き換える「デフォルト」値が必要な場合は、=
を使用して指定できます。
// default values
let [name = "Guest", surname = "Anonymous"] = ["Julius"];
alert(name); // Julius (from array)
alert(surname); // Anonymous (default used)
デフォルト値は、より複雑な式または関数呼び出しにすることができます。値が提供されていない場合にのみ評価されます。
たとえば、ここでは2つのデフォルトにprompt
関数を使用しています。
// runs only prompt for surname
let [name = prompt('name?'), surname = prompt('surname?')] = ["Julius"];
alert(name); // Julius (from array)
alert(surname); // whatever prompt gets
注:prompt
は、欠けている値(surname
)に対してのみ実行されます。
オブジェクトのデストラクチャリング
デストラクチャリング代入は、オブジェクトでも機能します。
基本的な構文は次のとおりです。
let {var1, var2} = {var1:…, var2:…}
右側に、変数に分割したい既存のオブジェクトが必要です。左側は、対応するプロパティのオブジェクトのような「パターン」を含んでいます。最も単純なケースでは、{...}
内の変数名のリストです。
たとえば
let options = {
title: "Menu",
width: 100,
height: 200
};
let {title, width, height} = options;
alert(title); // Menu
alert(width); // 100
alert(height); // 200
options.title
、options.width
、options.height
のプロパティは、対応する変数に割り当てられます。
順序は関係ありません。これも機能します。
// changed the order in let {...}
let {height, width, title} = { title: "Menu", height: 200, width: 100 }
左側のパターンはより複雑にすることができ、プロパティと変数の間のマッピングを指定します。
プロパティを変数に別の名前で割り当てる場合(たとえば、options.width
を変数w
に入れる場合)、コロンを使用して変数名を設定できます。
let options = {
title: "Menu",
width: 100,
height: 200
};
// { sourceProperty: targetVariable }
let {width: w, height: h, title} = options;
// width -> w
// height -> h
// title -> title
alert(title); // Menu
alert(w); // 100
alert(h); // 200
コロンは「何が:どこにいくか」を示します。上記の例では、width
プロパティはw
に、height
プロパティはh
に、title
は同じ名前に割り当てられます。
可能性のある欠落プロパティには、次のように"="
を使用してデフォルト値を設定できます。
let options = {
title: "Menu"
};
let {width = 100, height = 200, title} = options;
alert(title); // Menu
alert(width); // 100
alert(height); // 200
配列や関数のパラメーターと同様に、デフォルト値は任意の式または関数呼び出しにすることができます。値が提供されていない場合にのみ評価されます。
以下のコードでは、prompt
はwidth
を要求しますが、title
は要求しません。
let options = {
title: "Menu"
};
let {width = prompt("width?"), title = prompt("title?")} = options;
alert(title); // Menu
alert(width); // (whatever the result of prompt is)
コロンと等号の両方を使用することもできます。
let options = {
title: "Menu"
};
let {width: w = 100, height: h = 200, title} = options;
alert(title); // Menu
alert(w); // 100
alert(h); // 200
多くのプロパティを持つ複雑なオブジェクトがある場合、必要なものだけを抽出できます。
let options = {
title: "Menu",
width: 100,
height: 200
};
// only extract title as a variable
let { title } = options;
alert(title); // Menu
restパターン“…”
オブジェクトに変数よりも多くのプロパティがある場合はどうなりますか?いくつかを取得して、「残りの部分」をどこかに割り当てることができますか?
配列で行ったように、restパターンを使用できます。一部の古いブラウザー(IE)ではサポートされていません(Babelを使用してポリフィルします)が、最新のブラウザーでは機能します。
次のようになります。
let options = {
title: "Menu",
height: 200,
width: 100
};
// title = property named title
// rest = object with the rest of properties
let {title, ...rest} = options;
// now title="Menu", rest={height: 200, width: 100}
alert(rest.height); // 200
alert(rest.width); // 100
let
がない場合の注意点上記の例では、変数は代入時に直接宣言されました:let {…} = {…}
。もちろん、let
なしで既存の変数を使用することもできます。しかし、注意点があります。
これは機能しません。
let title, width, height;
// error in this line
{title, width, height} = {title: "Menu", width: 200, height: 100};
問題は、JavaScriptがメインのコードフロー(別の式内ではない)にある{...}
をコードブロックとして扱うことです。このようなコードブロックは、次のようにステートメントをグループ化するために使用できます。
{
// a code block
let message = "Hello";
// ...
alert( message );
}
そのため、ここではJavaScriptはコードブロックがあると想定しているため、エラーが発生します。代わりにデストラクチャリングが必要です。
JavaScriptにそれがコードブロックではないことを示すには、式を括弧(...)
で囲みます。
let title, width, height;
// okay now
({title, width, height} = {title: "Menu", width: 200, height: 100});
alert( title ); // Menu
ネストされたデストラクチャリング
オブジェクトまたは配列に他のネストされたオブジェクトや配列が含まれている場合、より複雑な左辺のパターンを使用して、より深い部分を抽出できます。
以下のコードでは、options
はsize
プロパティに別のオブジェクトを、items
プロパティに配列を持っています。代入の左側のパターンは、それらから値を抽出するための同じ構造を持っています。
let options = {
size: {
width: 100,
height: 200
},
items: ["Cake", "Donut"],
extra: true
};
// destructuring assignment split in multiple lines for clarity
let {
size: { // put size here
width,
height
},
items: [item1, item2], // assign items here
title = "Menu" // not present in the object (default value is used)
} = options;
alert(title); // Menu
alert(width); // 100
alert(height); // 200
alert(item1); // Cake
alert(item2); // Donut
左側にないextra
を除く、options
オブジェクトのすべてのプロパティは、対応する変数に割り当てられます。
最後に、デフォルト値からwidth
、height
、item1
、item2
、title
を取得します。
size
とitems
に対応する変数がないことに注意してください。代わりにその内容を取得しているためです。
スマートな関数パラメーター
関数が多数のパラメーターを持ち、そのほとんどがオプションである場合があります。これは、ユーザーインターフェースで特に当てはまります。メニューを作成する関数を考えてみてください。幅、高さ、タイトル、アイテムリストなどがある可能性があります。
このような関数を記述する悪い方法を次に示します。
function showMenu(title = "Untitled", width = 200, height = 100, items = []) {
// ...
}
実際には、引数の順序を覚える方法が問題です。通常、IDEは、特にコードが適切に文書化されている場合は、私たちを支援しようとしますが、それでも…もう1つの問題は、ほとんどのパラメーターがデフォルトで問題ない場合に関数を呼び出す方法です。
このような感じですか?
// undefined where default values are fine
showMenu("My Menu", undefined, undefined, ["Item1", "Item2"])
これは見苦しく、より多くのパラメーターを扱うと読みづらくなります。
デストラクチャリングが役に立ちます!
パラメーターをオブジェクトとして渡すことができ、関数はそれらをすぐに変数にデストラクチャリングします。
// we pass object to function
let options = {
title: "My menu",
items: ["Item1", "Item2"]
};
// ...and it immediately expands it to variables
function showMenu({title = "Untitled", width = 200, height = 100, items = []}) {
// title, items – taken from options,
// width, height – defaults used
alert( `${title} ${width} ${height}` ); // My Menu 200 100
alert( items ); // Item1, Item2
}
showMenu(options);
ネストされたオブジェクトとコロンマッピングを使用した、より複雑なデストラクチャリングを使用することもできます。
let options = {
title: "My menu",
items: ["Item1", "Item2"]
};
function showMenu({
title = "Untitled",
width: w = 100, // width goes to w
height: h = 200, // height goes to h
items: [item1, item2] // items first element goes to item1, second to item2
}) {
alert( `${title} ${w} ${h}` ); // My Menu 100 200
alert( item1 ); // Item1
alert( item2 ); // Item2
}
showMenu(options);
完全な構文は、デストラクチャリング代入の場合と同じです。
function({
incomingProperty: varName = defaultValue
...
})
次に、パラメーターのオブジェクトには、incomingProperty
プロパティの変数varName
があり、デフォルトではdefaultValue
が使用されます。
このようなデストラクチャリングは、showMenu()
に引数があることを前提としています。すべての値をデフォルトで使用する場合は、空のオブジェクトを指定する必要があります。
showMenu({}); // ok, all values are default
showMenu(); // this would give an error
パラメーターのオブジェクト全体に{}
をデフォルト値にすることで、これを修正できます。
function showMenu({ title = "Menu", width = 100, height = 200 } = {}) {
alert( `${title} ${width} ${height}` );
}
showMenu(); // Menu 100 200
上記のコードでは、引数のオブジェクト全体がデフォルトで{}
であるため、常にデストラクチャリングできるものがあります。
まとめ
-
デストラクチャリング代入により、オブジェクトまたは配列を多くの変数に瞬時にマッピングできます。
-
完全なオブジェクト構文
let {prop : varName = defaultValue, ...rest} = object
これは、
prop
プロパティを変数varName
に入れる必要があり、そのようなプロパティが存在しない場合は、default
値を使用する必要があることを意味します。マッピングがないオブジェクトプロパティは、
rest
オブジェクトにコピーされます。 -
完全な配列構文
let [item1 = defaultValue, item2, ...rest] = array
最初のアイテムは
item1
に、2番目のアイテムはitem2
に、残りはすべて配列rest
になります。 -
ネストされた配列やオブジェクトからデータを取り出すことができます。そのためには、左辺の構造と右辺の構造が同じでなければなりません。
コメント
<code>
タグを使用し、複数行の場合は<pre>
タグで囲み、10行を超える場合はサンドボックス(plnkr、jsbin、codepen…)を使用してください。