はじめに

ES6(ECMAScript 2015)で導入された「分割代入」と「スプレッド構文」は、モダンなJavaScript開発において欠かせない構文です。これらを使いこなすことで、配列やオブジェクトからの値の取り出し、データのコピーや結合が驚くほど簡潔に書けるようになります。

本記事では、以下の内容を初心者向けにわかりやすく解説します。

  • 分割代入の基本(配列・オブジェクト)
  • デフォルト値と変数名の変更
  • 残余パターン(Rest パターン)の使い方
  • スプレッド構文による配列・オブジェクトの展開
  • 実践的な活用パターンと注意点

分割代入とは

分割代入(Destructuring Assignment)とは、配列やオブジェクトから値を取り出して、個別の変数に代入するための構文です。従来は複数行で書いていた処理を、1行で簡潔に記述できます。

1
2
3
4
5
6
7
// 従来の書き方
const user = { name: "田中", age: 25 };
const name = user.name;
const age = user.age;

// 分割代入を使った書き方
const { name, age } = user;

分割代入を使うことで、コードが短くなるだけでなく、必要なプロパティが一目で分かる可読性の高いコードになります。

配列の分割代入

配列の分割代入では、配列の要素を順番に変数へ代入します。

基本的な使い方

配列の要素は、インデックス順に左から右へ変数に割り当てられます。

1
2
3
4
5
6
7
8
const colors = ["赤", "青", "緑"];

// 分割代入で各要素を変数に代入
const [first, second, third] = colors;

console.log(first);  // "赤"
console.log(second); // "青"
console.log(third);  // "緑"

必要な要素だけを取り出す

不要な要素はカンマを使ってスキップできます。

1
2
3
4
5
6
7
const numbers = [1, 2, 3, 4, 5];

// 1番目と3番目の要素だけ取得(2番目はスキップ)
const [first, , third] = numbers;

console.log(first);  // 1
console.log(third);  // 3

デフォルト値の設定

配列の要素が存在しない場合に備えて、デフォルト値を設定できます。

1
2
3
4
5
6
7
const fruits = ["りんご"];

// 2番目の要素がないため、デフォルト値が使用される
const [first, second = "みかん"] = fruits;

console.log(first);  // "りんご"
console.log(second); // "みかん"

変数の入れ替え

分割代入を使うと、一時変数なしで2つの変数の値を入れ替えられます。

1
2
3
4
5
6
7
8
let a = 1;
let b = 2;

// 変数の値を入れ替え
[a, b] = [b, a];

console.log(a); // 2
console.log(b); // 1

これは従来の方法(一時変数を使う)と比べて非常に簡潔です。

オブジェクトの分割代入

オブジェクトの分割代入では、プロパティ名に対応する変数に値を代入します。

基本的な使い方

プロパティ名と同じ名前の変数が自動的に作成されます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
const user = {
  name: "田中太郎",
  age: 25,
  email: "tanaka@example.com"
};

// オブジェクトから必要なプロパティを取り出す
const { name, age, email } = user;

console.log(name);  // "田中太郎"
console.log(age);   // 25
console.log(email); // "tanaka@example.com"

変数名を変更する

プロパティ名と異なる変数名を使いたい場合は、コロン(:)で指定します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
const user = {
  name: "田中太郎",
  age: 25
};

// nameプロパティをuserNameという変数名で受け取る
const { name: userName, age: userAge } = user;

console.log(userName); // "田中太郎"
console.log(userAge);  // 25

この書き方は、既存の変数名と衝突する場合や、より分かりやすい名前を付けたい場合に便利です。

デフォルト値の設定

プロパティが存在しない場合のデフォルト値を指定できます。

1
2
3
4
5
6
7
8
9
const user = {
  name: "田中太郎"
};

// ageが存在しないため、デフォルト値の20が使用される
const { name, age = 20 } = user;

console.log(name); // "田中太郎"
console.log(age);  // 20

変数名の変更とデフォルト値の組み合わせ

変数名の変更とデフォルト値は同時に指定できます。

1
2
3
4
5
6
7
8
9
const user = {
  name: "田中太郎"
};

// ageをuserAgeという変数名で、デフォルト値20で受け取る
const { name, age: userAge = 20 } = user;

console.log(name);    // "田中太郎"
console.log(userAge); // 20

ネストしたオブジェクトの分割代入

オブジェクトがネストしている場合も、入れ子構造で分割代入できます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
const user = {
  name: "田中太郎",
  address: {
    city: "東京",
    zipCode: "100-0001"
  }
};

// ネストしたaddressオブジェクトからcityを取り出す
const { name, address: { city } } = user;

console.log(name); // "田中太郎"
console.log(city); // "東京"

ネストが深くなりすぎると可読性が下がるため、2階層程度にとどめることをおすすめします。

残余パターン(Rest パターン)

残余パターンは、分割代入で取り出されなかった残りの要素をまとめて取得する構文です。...(ドット3つ)を使用します。

配列での残余パターン

1
2
3
4
5
6
7
8
const numbers = [1, 2, 3, 4, 5];

// 先頭の要素を取り出し、残りをrest配列に格納
const [first, second, ...rest] = numbers;

console.log(first);  // 1
console.log(second); // 2
console.log(rest);   // [3, 4, 5]

オブジェクトでの残余パターン

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
const user = {
  name: "田中太郎",
  age: 25,
  email: "tanaka@example.com",
  department: "開発部"
};

// nameとageを取り出し、残りをothersオブジェクトに格納
const { name, age, ...others } = user;

console.log(name);   // "田中太郎"
console.log(age);    // 25
console.log(others); // { email: "tanaka@example.com", department: "開発部" }

残余パターンは、特定のプロパティを除外した新しいオブジェクトを作成するのに便利です。

スプレッド構文とは

スプレッド構文(Spread Syntax)は、配列やオブジェクトの要素を展開する構文です。残余パターンと同じ...を使いますが、役割は逆です。

構文 役割 使用場所
残余パターン(Rest) 複数の要素を1つにまとめる 分割代入の左辺
スプレッド構文(Spread) 1つの配列・オブジェクトを展開する 配列リテラル・オブジェクトリテラル・関数呼び出しの中

配列のスプレッド構文

配列のコピー

スプレッド構文を使うと、配列の浅いコピー(シャローコピー)を簡単に作成できます。

1
2
3
4
5
6
7
const original = [1, 2, 3];

// 配列をコピー
const copy = [...original];

console.log(copy);            // [1, 2, 3]
console.log(original === copy); // false(別の配列)

配列の結合

複数の配列を1つに結合できます。

1
2
3
4
5
6
7
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];

// 2つの配列を結合
const combined = [...arr1, ...arr2];

console.log(combined); // [1, 2, 3, 4, 5, 6]

concat()メソッドよりも直感的で可読性が高い記法です。

配列への要素追加

既存の配列に要素を追加した新しい配列を作成できます。

1
2
3
4
5
6
const numbers = [2, 3, 4];

// 先頭と末尾に要素を追加
const extended = [1, ...numbers, 5];

console.log(extended); // [1, 2, 3, 4, 5]

元の配列を変更せずに新しい配列を作成できるため、イミュータブルな操作が可能です。

関数の引数として展開

配列の要素を個別の引数として関数に渡せます。

1
2
3
4
5
6
const numbers = [5, 2, 8, 1, 9];

// Math.maxに配列の要素を個別に渡す
const max = Math.max(...numbers);

console.log(max); // 9

apply()メソッドを使う必要がなくなり、コードがシンプルになります。

オブジェクトのスプレッド構文

オブジェクトのコピー

オブジェクトの浅いコピーを作成できます。

1
2
3
4
5
6
7
const original = { name: "田中", age: 25 };

// オブジェクトをコピー
const copy = { ...original };

console.log(copy);            // { name: "田中", age: 25 }
console.log(original === copy); // false(別のオブジェクト)

オブジェクトの結合(マージ)

複数のオブジェクトを1つに結合できます。

1
2
3
4
5
6
7
const defaults = { theme: "light", language: "ja" };
const userSettings = { theme: "dark" };

// デフォルト設定とユーザー設定をマージ
const settings = { ...defaults, ...userSettings };

console.log(settings); // { theme: "dark", language: "ja" }

後に指定したオブジェクトのプロパティが優先されます。

プロパティの上書き

既存のオブジェクトに新しいプロパティを追加したり、既存のプロパティを上書きできます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
const user = { name: "田中", age: 25 };

// ageを更新し、emailを追加
const updatedUser = { ...user, age: 26, email: "tanaka@example.com" };

console.log(updatedUser);
// { name: "田中", age: 26, email: "tanaka@example.com" }

// 元のオブジェクトは変更されない
console.log(user);
// { name: "田中", age: 25 }

元のオブジェクトを変更せずに更新できるため、Reactなどの状態管理で頻繁に使用されます。

関数の引数での活用

分割代入とスプレッド構文は、関数の引数でも活用できます。

引数でオブジェクトを分割代入

関数の引数でオブジェクトを分割代入すると、必要なプロパティだけを受け取れます。

1
2
3
4
5
6
7
8
// 引数でオブジェクトを分割代入
function greet({ name, age }) {
  console.log(`${name}さん(${age}歳)、こんにちは!`);
}

const user = { name: "田中", age: 25, email: "tanaka@example.com" };

greet(user); // "田中さん(25歳)、こんにちは!"

引数にデフォルト値を設定

引数の分割代入でもデフォルト値を設定できます。

1
2
3
4
5
6
7
function createUser({ name = "ゲスト", age = 20 } = {}) {
  return { name, age };
}

console.log(createUser({ name: "田中" }));  // { name: "田中", age: 20 }
console.log(createUser({}));               // { name: "ゲスト", age: 20 }
console.log(createUser());                 // { name: "ゲスト", age: 20 }

= {}を付けることで、引数が省略された場合も安全に動作します。

残余引数(Rest Parameters)

関数の引数で残余パターンを使用すると、可変長引数を受け取れます。

1
2
3
4
5
6
function sum(...numbers) {
  return numbers.reduce((total, num) => total + num, 0);
}

console.log(sum(1, 2, 3));       // 6
console.log(sum(1, 2, 3, 4, 5)); // 15

実践的な活用パターン

APIレスポンスからのデータ抽出

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// APIレスポンスを想定したオブジェクト
const response = {
  status: 200,
  data: {
    user: {
      id: 1,
      name: "田中太郎",
      profile: {
        bio: "Webエンジニア",
        location: "東京"
      }
    },
    posts: [
      { id: 1, title: "記事1" },
      { id: 2, title: "記事2" }
    ]
  }
};

// 必要なデータを分割代入で抽出
const { data: { user: { name, profile: { bio } }, posts } } = response;

console.log(name);  // "田中太郎"
console.log(bio);   // "Webエンジニア"
console.log(posts); // [{ id: 1, title: "記事1" }, { id: 2, title: "記事2" }]

配列の先頭・末尾を取り出す

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
const items = ["A", "B", "C", "D", "E"];

// 先頭を取得
const [first, ...rest] = items;
console.log(first); // "A"
console.log(rest);  // ["B", "C", "D", "E"]

// 末尾を取得(reverse()との組み合わせは元の配列を変更するので注意)
const copy = [...items];
const [last, ...others] = copy.reverse();
console.log(last);   // "E"
console.log(others); // ["D", "C", "B", "A"]

不変性を保ったオブジェクト更新

Reactなどのフレームワークでよく使用されるパターンです。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
const state = {
  user: { name: "田中", age: 25 },
  isLoading: false
};

// userのageだけを更新(不変性を保つ)
const newState = {
  ...state,
  user: {
    ...state.user,
    age: 26
  }
};

console.log(state.user.age);    // 25(元の状態は変更されない)
console.log(newState.user.age); // 26

配列から特定要素を除外

1
2
3
4
5
6
const fruits = ["りんご", "みかん", "ぶどう", "もも"];

// インデックス1(みかん)を除外
const withoutOrange = [...fruits.slice(0, 1), ...fruits.slice(2)];

console.log(withoutOrange); // ["りんご", "ぶどう", "もも"]

filter()を使った方法もあります。

1
const withoutOrange = fruits.filter((_, index) => index !== 1);

注意点とよくある間違い

シャローコピーである点に注意

スプレッド構文によるコピーは浅いコピー(シャローコピー)です。ネストしたオブジェクトや配列は参照がコピーされるため、変更すると元のオブジェクトにも影響します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
const original = {
  name: "田中",
  tags: ["JavaScript", "React"]
};

const copy = { ...original };

// tagsは参照がコピーされているため、元のオブジェクトにも影響する
copy.tags.push("TypeScript");

console.log(original.tags); // ["JavaScript", "React", "TypeScript"](変更されている)

深いコピー(ディープコピー)が必要な場合は、structuredClone()を使用します。

1
2
3
4
5
const deepCopy = structuredClone(original);
deepCopy.tags.push("Vue");

console.log(original.tags); // 変更されない
console.log(deepCopy.tags); // ["JavaScript", "React", "TypeScript", "Vue"]

分割代入時のundefined

存在しないプロパティを分割代入するとundefinedになります。デフォルト値を設定して対処しましょう。

1
2
3
4
5
6
const user = { name: "田中" };

// ageプロパティは存在しない
const { name, age } = user;

console.log(age); // undefined

オブジェクトの分割代入と宣言

すでに宣言された変数にオブジェクトの分割代入を行う場合、全体を括弧で囲む必要があります。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
let name, age;

// 括弧で囲まないとエラーになる
// { name, age } = { name: "田中", age: 25 }; // SyntaxError

// 正しい書き方
({ name, age } = { name: "田中", age: 25 });

console.log(name); // "田中"
console.log(age);  // 25

これはJavaScriptが{をブロック文の開始と解釈するためです。

分割代入とスプレッド構文の使い分け

以下の表を参考に、適切な構文を選択してください。

やりたいこと 使用する構文
配列・オブジェクトから値を取り出す 分割代入
残りの要素をまとめて取得する 残余パターン(Rest)
配列・オブジェクトをコピーする スプレッド構文
配列・オブジェクトを結合する スプレッド構文
関数に配列を展開して渡す スプレッド構文
可変長引数を受け取る 残余引数(Rest Parameters)

まとめ

本記事では、JavaScriptの分割代入とスプレッド構文について解説しました。

分割代入のポイント

  • 配列は順番、オブジェクトはプロパティ名で値を取り出す
  • デフォルト値で安全に値を取得できる
  • 変数名の変更やネストした構造にも対応

スプレッド構文のポイント

  • 配列・オブジェクトの展開、コピー、結合に使用
  • シャローコピーである点に注意
  • 不変性を保った更新パターンで活用

これらの構文を使いこなすことで、より簡潔で可読性の高いモダンなJavaScriptコードを書けるようになります。ぜひ実際のコードで積極的に活用してみてください。

参考リンク