配列やオブジェクトから値を取り出して変数に入れるとき、1つずつインデックスやプロパティを指定して書くと、どうしても記述が長くなりがちです。そんなときに役立つのが分割代入(Destructuring)です。この記事では、配列・オブジェクトから値を取り出す基本から、別名やデフォルト値の付け方、ネストした分割代入、関数引数での使い方、残余(rest)との組み合わせ、2つの変数の入れ替えまで、初心者にもわかるように順番に解説します。
目次
分割代入(Destructuring)とは
分割代入は、配列やオブジェクトの中身を取り出して、それぞれ別々の変数に代入するための書き方です。これまで const name = user.name; のように1行ずつ書いていたものを、まとめて1行で書けるようになります。「中身を分けて(分割して)代入する」というイメージのとおり、ひとかたまりのデータを個々の変数にばらして受け取れるのが特徴です。
なお、ピリオド3つで書くスプレッド構文(...)が「値を展開して並べる(出す側)」だったのに対し、分割代入は「並んだ値を変数で受け取る(取り出す側)」の書き方だと考えると整理しやすいでしょう。
配列の分割代入
配列の場合は、代入する左辺を角かっこ [ ] で書きます。配列の先頭から順番に、それぞれの変数へ値が入っていきます。取り出す位置は順番で決まるため、変数名は自由に付けられます。
const colors = ["red", "green", "blue"];
// 先頭から順番に変数へ代入される
const [first, second, third] = colors;
console.log(first); // "red"
console.log(second); // "green"
console.log(third); // "blue"
不要な要素はカンマだけを置いて読み飛ばせます。たとえば2番目だけが欲しいときは、次のように書きます。
const colors = ["red", "green", "blue"];
// 1番目を飛ばして、2番目だけを取り出す
const [, second] = colors;
console.log(second); // "green"
オブジェクトの分割代入
オブジェクトの場合は、左辺を波かっこ { } で書きます。配列と違い、順番ではなくプロパティ名で取り出す点が大きな違いです。そのため、変数名は取り出したいプロパティ名と同じにする必要があります。
const user = { name: "Taro", age: 20 };
// プロパティ名と同じ名前の変数に取り出す
const { name, age } = user;
console.log(name); // "Taro"
console.log(age); // 20
プロパティ名で取り出すため、書く順番は自由です。const { age, name } = user; のように入れ替えても、それぞれ正しいプロパティの値が入ります。
別名(エイリアス)を付けて取り出す
プロパティ名とは違う変数名で受け取りたいときは、プロパティ名: 新しい変数名 の形で別名を付けられます。すでに同じ名前の変数がある場合や、より分かりやすい名前にしたい場合に便利です。
const user = { name: "Taro", age: 20 };
// name を userName、age を userAge という変数名で受け取る
const { name: userName, age: userAge } = user;
console.log(userName); // "Taro"
console.log(userAge); // 20
ここで注意したいのは、別名を付けると元のプロパティ名の変数(この例では name や age)は作られないという点です。値は新しい名前の変数にだけ入ります。
デフォルト値を指定する
取り出したいプロパティが存在しないとき、変数には undefined が入ります。これを避けたいときは、= でデフォルト値を指定できます。プロパティが見つからなかった場合だけ、その値が使われます。
const user = { name: "Taro" };
// role は存在しないので、デフォルト値の "guest" が使われる
const { name, role = "guest" } = user;
console.log(name); // "Taro"
console.log(role); // "guest"
別名とデフォルト値は同時に使えます。その場合は プロパティ名: 新しい変数名 = デフォルト値 の順で書きます(例: const { role: userRole = "guest" } = user;)。
デフォルト値が使われるのは undefined のときだけ
デフォルト値でつまずきやすいのが、適用される条件です。デフォルト値が使われるのは、取り出した値が undefined のとき(=プロパティが存在しない、または値が明示的に undefined のとき)だけです。値が null や 0、空文字 "" の場合は「値が存在する」とみなされ、デフォルト値は使われません。
// 値が null のケース
const data = { count: null };
// null は undefined ではないため、デフォルト値は使われない
const { count = 0 } = data;
console.log(count); // null(0 にはならない)
null のときも初期値に置き換えたい場合は、分割代入のデフォルト値ではなく、取り出したあとに ??(Null 合体演算子)などで別途処理する必要があります。「デフォルト値は undefined 専用」と覚えておくと、思わぬ値が残るトラブルを避けられます。
ネストした分割代入
オブジェクトの中にオブジェクトが入れ子(ネスト)になっている場合も、構造に合わせて波かっこを重ねることで、奥にある値を直接取り出せます。
const user = {
name: "Taro",
address: {
city: "Tokyo",
zip: "100-0001",
},
};
// address の中の city を直接取り出す
const { name, address: { city } } = user;
console.log(name); // "Taro"
console.log(city); // "Tokyo"
ここで間違えやすいのが、address: { city } と書いたとき、address という変数は作られないという点です。この address: は「address の中をさらに分解する」という指示であり、city だけが変数として作られます。address 自体も使いたい場合は、別途 const { address } = user; のように取り出してください。
関数の引数で分割代入する
分割代入は、関数の引数を受け取る部分でも使えます。オブジェクトを引数として渡し、その場で必要なプロパティだけを取り出せるため、options.width・options.height のように毎回 options. を書く手間が省けます。
// 引数のオブジェクトから width と height を取り出す
function createBox({ width, height }) {
return width * height;
}
const size = { width: 100, height: 50 };
console.log(createBox(size)); // 5000
引数の分割代入でもデフォルト値を組み合わせられます。指定されなかったプロパティに初期値を与えられるので、設定オブジェクトを受け取る関数で重宝します。
// color が渡されなければ "black" を使う
function draw({ width, height, color = "black" }) {
console.log(`${width}x${height} / ${color}`);
}
draw({ width: 100, height: 50 }); // 100x50 / black
draw({ width: 100, height: 50, color: "red" }); // 100x50 / red
残余(rest)と組み合わせて残りをまとめる
取り出したい値の後ろに ... を付けると、残りの要素やプロパティをまとめて1つにできます。これを残余(rest)と呼びます。配列なら残りが配列に、オブジェクトなら残りがオブジェクトにまとまります。
const numbers = [1, 2, 3, 4, 5];
// 先頭を取り出し、残りは rest という配列にまとめる
const [head, ...rest] = numbers;
console.log(head); // 1
console.log(rest); // [2, 3, 4, 5]
const user = { id: 1, name: "Taro", age: 20 };
// id を取り出し、残りのプロパティは others にまとめる
const { id, ...others } = user;
console.log(id); // 1
console.log(others); // { name: "Taro", age: 20 }
残余は必ず末尾に1つだけ書きます。[...rest, last] のように途中に書くと構文エラーになる点に注意してください。
2つの変数の値を入れ替える
配列の分割代入を使うと、一時変数を用意せずに2つの変数の値を入れ替える(スワップする)ことができます。右辺で配列を作り、それを左辺の2つの変数に割り当て直すという仕組みです。
let a = 1;
let b = 2;
// 一時変数なしで値を入れ替える
[a, b] = [b, a];
console.log(a); // 2
console.log(b); // 1
文の先頭で { } を使うとエラーになる
すでに宣言した変数に、オブジェクトの分割代入で値を入れ直したいことがあります。このとき const や let を付けずに文の先頭で { から書き始めると、エラーになります。JavaScript が行頭の { を「分割代入」ではなく「コードブロックの始まり」と解釈してしまうためです。
let name, age;
const user = { name: "Taro", age: 20 };
// 行頭の { がブロックと解釈され、構文エラーになる
// { name, age } = user; // SyntaxError
// 全体を ( ) で囲むと、分割代入として正しく動く
({ name, age } = user);
console.log(name); // "Taro"
console.log(age); // 20
このように、宣言キーワードなしでオブジェクトの分割代入をするときは、文全体を丸かっこ ( ) で囲みます。const { name } = user; のように宣言とセットで書く場合は行頭が const になるため、この問題は起きません。
まとめ
分割代入を使うと、配列やオブジェクトから必要な値を、短く読みやすいコードで取り出せます。
- 配列は
[ ]で順番に、オブジェクトは{ }でプロパティ名で取り出す - オブジェクトは
プロパティ名: 別名で別の変数名にでき、= 値でデフォルト値を指定できる - デフォルト値が使われるのは値が
undefinedのときだけで、nullには適用されない - ネストや関数の引数でも使え、
...(残余)で残りをまとめられる - 宣言なしのオブジェクト分割代入は、文全体を
( )で囲む
まずはオブジェクトからのプロパティ取り出しや、関数引数での分割代入から試して、すっきりとした書き方に慣れていきましょう。