---
type: reference
language: javascript
slug: operator/spread
title: "スプレッド構文と残余 — ... の2つの意味"
title_tag: "JavaScript の ... — スプレッドと残余引数の違い"
summary: >
  同じ ... が、広げる側と集める側で逆の意味になります。配列・オブジェクト・関数それぞれの
  使い方、後ろ勝ちの上書き、複製が1段しか効かない点までを実行して確かめます。
description: >
  ... は置く場所によって「広げる」と「集める」の逆の意味になります。後ろ勝ちの上書き、
  複製が1段しか効かない点、配列をオブジェクトに広げたときの形まで実行して示します。
status: published
difficulty: 2
minutes: 8

versions:
  verified: "Node 22.22.3"
  since: "ES2018"
  deprecated: null
  removed: null

sources:
  - title: "Spread — ECMAScript® 2026 Language Specification"
    url: "https://tc39.es/ecma262/#prod-SpreadElement"
  - title: "Rest Parameters — ECMAScript® 2026 Language Specification"
    url: "https://tc39.es/ecma262/#sec-parameter-lists"
  - title: "スプレッド構文 — MDN"
    url: "https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Spread_syntax"

terms: [スプレッド構文, 残余, 浅いコピー, 反復可能]

links:
  related:
    - javascript/errors/is-not-iterable
    - javascript/reference/array/from
    - javascript/reference/iterator/iterable
    - javascript/reference/object/assign
    - javascript/how-to/object/merge
    - javascript/reference/function/parameters
    - javascript/reference/array/push

content_updated_at: 2026-09-08
published_at: 2026-09-08
---

`...` には[num:2]つの意味があります。**置く場所で決まります。**

```js run
console.log([...[1, 2], 3]);

const [first, ...rest] = [1, 2, 3];

console.log(first, rest);
```
```output
[ 1, 2, 3 ]
1 [ 2, 3 ]
```

- **右辺**（値を作る側）… [key:広げる]（スプレッド）
- **左辺**（受け取る側）… [key:集める]（残余）

同じ記号で、**逆の働き**です。

## 配列を広げる

```js run
const nums = [1, 2];

console.log([...nums, 3]);
console.log([0, ...nums]);
console.log([...nums, ...nums]);
console.log(Math.max(...[1, 5, 3]));
```
```output
[ 1, 2, 3 ]
[ 0, 1, 2 ]
[ 1, 2, 1, 2 ]
5
```

**関数の引数としても広げられます。** `Math.max()` は配列を受け取らないので、
`...` で1つずつに開いてから渡します。

[dim:要素が非常に多いと、引数の上限に当たることがあります。件数が読めないときは `reduce()` を使ってください。]

## 文字列も広げられる

```js run
console.log([...'あい']);
console.log([...new Set([1, 1, 2])]);
console.log([...new Map([['a', 1]])]);
```
```output
[ 'あ', 'い' ]
[ 1, 2 ]
[ [ 'a', 1 ] ]
```

**広げられるのは[type:反復可能]なもの**です。配列・文字列・`Set`・`Map` が該当します。

文字列は[key:符号位置ごと]に分かれるので、絵文字が割れません。
→ [文字を正しく数える](/ja/javascript/learn/string/unicode/)

## オブジェクトを広げる

```js run
console.log({ ...{ a: 1 }, b: 2 });
console.log({ ...{ a: 1 }, ...{ a: 2 } });
console.log({ a: 1, ...{ a: 2 } });
console.log({ ...{ a: 2 }, a: 1 });
```
```output
{ a: 1, b: 2 }
{ a: 2 }
{ a: 2 }
{ a: 1 }
```

**同じ鍵があれば[key:後ろが勝ちます]。**
既定値を先に、上書きを後ろに置く、という順で書きます。

```js run
function withDefaults(options) {
  return { retry: 1, timeout: 1000, ...options };
}

console.log(withDefaults({ timeout: 50 }));
console.log(withDefaults({}));
```
```output
{ retry: 1, timeout: 50 }
{ retry: 1, timeout: 1000 }
```

`null` や `undefined` を広げても[em:落ちません]。

```js run
console.log({ ...null, ...undefined, a: 1 });
```
```output
{ a: 1 }
```

`Object.keys(null)` は落ちるのに、[bad:こちらは落ちません]。
この差が、原因の切り分けを難しくします。
→ [TypeError: Cannot convert undefined or null to object](/ja/javascript/errors/cannot-convert-undefined-or-null/)

## 配列をオブジェクトに広げると

```js run
console.log({ ...[1, 2] });
```
```output
{ '0': 1, '1': 2 }
```

**添字が鍵になります。** 意図してやることは、まずありません。
[bad:配列を渡すつもりでオブジェクトに広げていないか]確かめてください。

## 集める側

### 分割代入で

```js run
const [first, ...rest] = [1, 2, 3];
const { a, ...others } = { a: 1, b: 2, c: 3 };

console.log(first, rest);
console.log(a, others);
```
```output
1 [ 2, 3 ]
1 { b: 2, c: 3 }
```

**残りをまとめて受け取れます。**
鍵を1つ除いたオブジェクトを作るのに、いちばん短い書き方です。
→ [オブジェクトから必要な鍵だけ取り出す・除く](/ja/javascript/how-to/object/pick/)

### 関数の引数で

```js run
function sum(...nums) {
  return nums.reduce((total, n) => total + n, 0);
}

console.log(sum(1, 2, 3));
console.log(sum());
```
```output
6
0
```

**渡されなければ空配列**です。
→ [関数の引数 — 既定値・可変長・分割代入](/ja/javascript/reference/function/parameters/)

## いちばんの落とし穴：1段しか複製されない

```js bad
const original = { x: { y: 1 } };
const copied = { ...original };

copied.x.y = 99;

console.log(original.x.y);
```
```output
99
```

`copied` は新しいオブジェクトですが、**`x` は同じものを指しています。**
これを[type:浅いコピー]と呼びます。

深く複製するなら `structuredClone()` です。

```js run
const original = { x: { y: 1 } };
const copied = structuredClone(original);

copied.x.y = 99;

console.log(original.x.y, copied.x.y);
```
```output
1 99
```

→ [オブジェクトを結合する](/ja/javascript/how-to/object/merge/)

## よくある間違い

### 関数の途中に残余を書く

残余引数は[bad:最後にしか置けません]。

```js
function wrong(...rest, last) {}
```

これは**構文として読めません**。実行する前に弾かれます。
[dim:構文の誤りなので、このページでは実行例にできません。]

### `push` の代わりに使って遅くする

```js run
let items = [];

for (const n of [1, 2, 3]) {
  items = [...items, n];
}

console.log(items);
```
```output
[ 1, 2, 3 ]
```

結果は正しいのですが、**毎回すべてを作り直しています。**
件数が増えると急に遅くなります。

繰り返しの中で増やすなら `push()` を使ってください。
→ [push() / pop() / shift() / unshift()](/ja/javascript/reference/array/push/)

## まとめ

- 同じ `...` が、**右辺では広げ、左辺では集める**
- 広げられるのは[type:反復可能]なもの（配列・文字列・`Set`・`Map`）
- オブジェクトは**後ろが勝つ**。既定値を先に書く
- `null` / `undefined` を広げても落ちない
- 配列をオブジェクトに広げると[bad:添字が鍵]になる
- **複製は1段だけ。** 深く複製するなら `structuredClone()`
- 繰り返しの中で `[...items, n]` を使わない
