JavaScript | ES6+ 文法:分割代入 – rest 要素

JavaScript
スポンサーリンク

rest 要素とは何か

rest 要素(残余要素)は、分割代入の左側で ...rest のように書き、“取りきれなかった残り全部”を配列やオブジェクトとしてまとめて受ける記法です。ここが重要です:配列なら残り要素を“配列”に、オブジェクトなら残りプロパティを“新しいオブジェクト”に集めます。可変長データや「先頭だけ主役、あとは全部まとめて」というパターンで威力を発揮します。

// 配列の残余
const [head, ...tail] = [10, 20, 30];
console.log(head); // 10
console.log(tail); // [20, 30]

// オブジェクトの残余
const user = { id: 7, name: "Alice", role: "admin" };
const { id, ...rest } = user;
console.log(id);   // 7
console.log(rest); // { name: "Alice", role: "admin" }
JavaScript

配列の分割代入での rest 要素

基本の受け方(先頭+残り全部)

先頭や特定要素だけ個別に取り、他は ...rest でまとめます。ここが重要です:残余は“最後の位置に一つだけ”置けます。

const [first, ...others] = [1, 2, 3, 4];
console.log(first);  // 1
console.log(others); // [2, 3, 4]
JavaScript

スキップと組み合わせる

不要な位置はカンマで飛ばし、残りはまとめます。配列の柔軟な取り回しに有効です。

const [head, , ...tail] = [10, 20, 30, 40];
console.log(head); // 10
console.log(tail); // [30, 40]
JavaScript

不足や空配列への振る舞い

右側が短い・空でも安全です。残余は常に“配列”として返り、空なら [] になります。

const [x, ...xs] = [1];
console.log(x, xs); // 1 []
const [y, ...ys] = [];
console.log(y, ys); // undefined []
JavaScript

オブジェクトの分割代入での rest 要素

取り出し以外を“ひとまとめ”にする

指定したプロパティ以外を新しいオブジェクトに集めます。ここが重要です:浅いコピーであり、元のオブジェクトは変更されません。

const cfg = { theme: "dark", fontSize: 16, debug: true };
const { theme, ...options } = cfg;
console.log(theme);   // "dark"
console.log(options); // { fontSize: 16, debug: true }
JavaScript

名前変更や既定値との併用

主役を取り出す際に別名・既定値を付け、残りはまとめて渡せます。

const apiUser = { user_id: 7, display_name: undefined, role: "admin" };
const { user_id: id, display_name: name = "", ...meta } = apiUser;
console.log(id, name, meta); // 7 "" { role: "admin" }
JavaScript

rest と spread の違い(受ける側 vs 渡す側)

rest は“受け取り側(分割代入の左)”で使い、残りをまとめます。spread(...)は“渡す側(リテラルの右)”で使い、配列やオブジェクトを展開します。ここが重要です:役割が真逆なので、書く位置で意味が変わります。

// rest(受ける側)
const [a, ...restArr] = [1, 2, 3];
const { x, ...restObj } = { x: 1, y: 2, z: 3 };

// spread(渡す側)
const arr = [0, ...restArr];         // [0, 2, 3]
const obj = { a: 0, ...restObj };    // { a: 0, y: 2, z: 3 }
JavaScript

制約・評価・既定値のポイント(重要部分の深掘り)

位置の制約(最後に一つだけ)

配列の残余は最後の位置に1つだけ置けます。途中や複数は構文エラーです。オブジェクトでも同様に、残余は最後に一つだけ。

// const [a, ...r, b] = [1,2,3]; // 構文エラー
// const { a, ...r, b } = obj;   // 構文エラー
JavaScript

既定値との併用

残余そのものに既定値を付ける必要はありません(常に配列/オブジェクトとして生成されます)。個別に受ける要素やプロパティには既定値を付けられます。

const [head = 0, ...tail] = []; // head=0, tail=[]
const { name = "N/A", ...rest } = {}; // name="N/A", rest={}
JavaScript

浅いコピーであること

オブジェクトの残余は浅いコピーです。ネストした参照は元のオブジェクトと共有されます。ここが重要です:深い変更が必要なら、別途ディープコピーを検討します。

const src = { a: { nested: 1 }, b: 2 };
const { a, ...rest } = src;
a.nested = 99;
console.log(src.a.nested); // 99(共有される)
JavaScript

評価とパフォーマンス

右側の配列・オブジェクトは評価された後、残余が構築されます。重い計算結果を多数含む場合、不要なものまで生成することがあります。必要な要素だけを前処理で絞る設計が効率的です。


実務で効くパターン

先頭は主役、残りはまとめて渡す

イベントやメッセージ処理で、先頭にタイプやヘッダを置き、残りをペイロードとして扱います。

const handle = ([type = "unknown", ...payloads] = []) =>
  `type=${type}, payloads=${payloads.length}`;
console.log(handle(["loaded", { id: 1 }, { cache: true }]));
JavaScript

オブジェクトで“主役抜き+拡張に強い”設計

必要なキーだけ抜き、残りをそのまま次の処理へ渡すと、拡張に強く保守しやすいです。

function renderUser({ id, name = "", ...attrs } = {}) {
  const label = `${id}:${name.trim()}`;
  // attrs に追加情報(role, active など)がまとまっている
  return { label, attrs };
}
console.log(renderUser({ id: 7, name: "  Alice  ", role: "admin", active: true }));
JavaScript

ロギングや追跡で“主情報+その他”に分ける

主なフィールドは明示的に出力し、その他はまとめて記録します。

const log = ({ message, ...meta }) => {
  console.log("msg:", message, "meta:", JSON.stringify(meta));
};
log({ message: "started", pid: 123, env: "prod" });
JavaScript

よくある落とし穴と回避策

残余に依存しすぎて意味が曖昧になる

「全部まとめる」が便利すぎると、何が必要で何が任意かが伝わりにくくなります。主役は明示的に取り、残りは“補助的情報”として扱う設計に。

深い構造の変更が伝播する(浅いコピー問題)

オブジェクトの残余は浅いコピー。ネストの変更が元に影響することを理解し、必要ならディープコピーを使う。

配列の残余で順序依存が強すぎる

残余は順序に依存します。意味が重要なら、配列よりオブジェクトで受けるほうが可読性が高い場合があります。


例題で理解を固める

// 1) 配列:ヘッダ+ペイロード
const toReport = ([header = "N/A", ...payloads] = []) =>
  `header=${header}, count=${payloads.length}`;
console.log(toReport(["INFO", { id: 1 }, { ok: true }]));

// 2) 配列:先頭だけ取り、残りを加工
const [first, ...rest] = [100, 200, 300];
const doubled = rest.map(x => x * 2);
console.log(first, doubled); // 100 [400, 600]

// 3) オブジェクト:主役抜き+残りをそのまま渡す
const product = { id: 1, name: "Apple", price: 100, tag: "fruit" };
const { id, name, ...others } = product;
console.log(id, name, others); // 1 "Apple" { price: 100, tag: "fruit" }

// 4) 名前変更+残余で API を自語彙へ
const apiRow = { product_id: 1, product_name: " Banana ", price_jpy: 200, stock: 5 };
const { product_id: id2, product_name: title, ...rest2 } = apiRow;
console.log(id2, title.trim(), rest2); // 1 "Banana" { price_jpy: 200, stock: 5 }

// 5) 関数引数:拡張に強い受け方
function renderRow({ id, name = "", price = 0, ...extra } = {}) {
  const line = `${id}:${name.trim()} (${price.toLocaleString("ja-JP")} 円)`;
  return { line, extra }; // extra に未知の追加フィールドを保持
}
console.log(renderRow({ id: 2, name: "  Orange  ", price: 300, sale: true }));
JavaScript

まとめ

rest 要素の核心は「分割代入で主役だけ抜き、残り全部を安全にひとまとめにする」ことです。配列なら残余は配列、オブジェクトなら浅いコピーの新オブジェクト。最後に一つだけという制約を守り、既定値は個別要素に付ける。spread は“渡す側”、rest は“受ける側”という役割の違いを理解し、主情報は明示、残余は補助情報として扱う設計にすると、初心者でも拡張に強く読みやすい ES6+ のコードが書けます。

タイトルとURLをコピーしました