スプレッド構文

スプレッド構文を使うと、配列式や文字列などの反復可能オブジェクトを、 0 個以上の引数 (関数呼び出しの場合) や要素 (配列リテラルの場合) を期待された場所で展開したり、オブジェクト式を、 0 個以上のキーと値のペア (オブジェクトリテラルの場合) を期待された場所で展開したりすることができます。

構文

関数呼び出しの場合:

myFunction(...iterableObj);

配列リテラルや文字列の場合:

[...iterableObj, '4', 'five', 6];

オブジェクトリテラルの場合 (ECMAScript 2018 の新機能)

let objClone = { ...obj };

残余構文 (引数)

残余構文はスプレッド構文と外見がよく似ていますが、配列やオブジェクトの分割代入に使われます。

こちらはスプレッド構文とは逆の働きといえます。スプレッド構文が要素を展開するのに対して、残余構文は複数の要素を集約して 1 つのオブジェクトに「濃縮」します。残余引数を参照してください。

関数呼び出しでの展開

apply() を置き換える

配列の要素を引数にして関数を呼び出すには Function.prototype.apply() を使うのが一般的です。

function myFunction(x, y, z) { }
const args = [0, 1, 2];
myFunction.apply(null, args);

スプレッド構文を使うと、上のコードは次のように書くことができます。

function myFunction(x, y, z) { }
const args = [0, 1, 2];
myFunction(...args);

スプレッド構文は、引数の何番目でも使えます。また、複数回使えます。

function myFunction(v, w, x, y, z) { }
const args = [0, 1];
myFunction(-1, ...args, 2, ...[3]);

new 演算子の適用

new によってコンストラクターを呼び出すとき、配列と apply()直接使用することはできません (apply()[[Call]] を実行するのであり [[Construct]] ではない)。ただし、配列はスプレッド構文のおかげで簡単に new を使用することができます。

const dateFields = [1970, 0, 1];  // 1 Jan 1970
const d = new Date(...dateFields);

スプレッド構文を使わずに同じ結果を得るには、専用の関数を使う間接的な手段を取らざるをえません。

function applyAndNew(constructor, args) {
   function partial () {
      return constructor.apply(this, args);
   };
   if (typeof constructor.prototype === "object") {
      partial.prototype = Object.create(constructor.prototype);
   }
   return partial;
}


function myConstructor () {
   console.log("arguments.length: " + arguments.length);
   console.log(arguments);
   this.prop1="val1";
   this.prop2="val2";
};

const myArguments = ["hi", "how", "are", "you", "mr", null];
const myConstructorWithArguments = applyAndNew(myConstructor, myArguments);

console.log(new myConstructorWithArguments);
//  (internal log of myConstructor):           arguments.length: 6
//  (internal log of myConstructor):           ["hi", "how", "are", "you", "mr", null]
//  (log of "new myConstructorWithArguments"): {prop1: "val1", prop2: "val2"}

配列リテラルでのスプレッド構文

より強力な配列リテラル

スプレッド構文を使用しない場合、既存の配列を一部として使用して新しい配列を作成するには、配列リテラル構文は十分ではなく、 push(), splice(), concat() などを組み合わせて使う高圧的なコードを使用しなければなりません。

const parts = ['shoulders', 'knees']; 
const lyrics = ['head', ...parts, 'and', 'toes']; 
//  ["head", "shoulders", "knees", "and", "toes"]

関数の引数と同様に、 ... は配列リテラルのどこでも、何回でも使えます。

配列を複製する

const arr = [1, 2, 3];
const arr2 = [...arr]; // arr.slice() のような動きです

arr2.push(4);
//  arr2 は [1, 2, 3, 4] になります
//  arr は変更されません

メモ: コピーは 1 段階の深さで行われます。そのため、次の例のような多次元配列のようなオブジェクトをコピーする場合には適さないでしょう。 (Object.assign() についても同じことが言えます。)

const a = [[1], [2], [3]];
const b = [...a];

b.shift().shift(); 
//  1

// あらら、配列 'a' も影響を受けちゃった。
a
//  [[], [2], [3]]

配列を連結するより良い方法

ある配列を既存の配列の末尾に連結するには、 Array.prototype.concat() がよく使われます。スプレッド構文を使用しないと、これは次のように行われます。

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

// arr2 のすべての要素を arr1 に追加する
arr1 = arr1.concat(arr2);

スプレッド構文を使うと、次のように書けます。

let arr1 = [0, 1, 2];
let arr2 = [3, 4, 5];

arr1 = [...arr1, ...arr2]; 
//  arr1 は [0, 1, 2, 3, 4, 5] となる
// 注: これ以外に const を使用すると、 TypeError (invalid assignment) が発生します

Array.prototype.unshift() は、値の配列を既存の配列の先頭に挿入するためによく使われます。スプレッド構文を使用しないと、これは次のように行われます。

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

//  arr2 のすべての要素を arr1 へ移植します
Array.prototype.unshift.apply(arr1, arr2)

//  arr1 is now [3, 4, 5, 0, 1, 2]

スプレッド構文を使うと、次のようになります。

let arr1 = [0, 1, 2];
let arr2 = [3, 4, 5];

arr1 = [...arr2, ...arr1];
//  arr1 is now [3, 4, 5, 0, 1, 2]

メモ: unshift() とは異なり、これは新しい arr1 を生成しており、その場では元の arr1 を変更しません

Object リテラルで使う

Rest/Spread Properties for ECMAScript proposal (ES2018) では、オブジェクトリテラルでのスプレッド構文が追加されています。スプレッド構文の対象となるオブジェクトの列挙可能なプロパティを、新しいオブジェクトにコピーします。

浅いコピー (プロトタイプを除く) の作成や、マージしたオブジェクトの作成が Object.assign() を使うよりも短いコードで書けます。

const obj1 = { foo: 'bar', x: 42 };
const obj2 = { foo: 'baz', y: 13 };

const clonedObj = { ...obj1 };
// Object { foo: "bar", x: 42 }

const mergedObj = { ...obj1, ...obj2 };
// Object { foo: "baz", x: 42, y: 13 }

Object.assign()セッターを起動しますが、スプレッド構文は起動しないことに注意してください。

スプレッド構文は Object.assign() 関数を置き換えたり模倣することはできないことに注意してください。

let obj1 = { foo: 'bar', x: 42 };
let obj2 = { foo: 'baz', y: 13 };
const merge = ( ...objects ) => ( { ...objects } );

let mergedObj1 = merge (obj1, obj2);
// Object { 0: { foo: 'bar', x: 42 }, 1: { foo: 'baz', y: 13 } }

let mergedObj2 = merge ({}, obj1, obj2);
// Object { 0: {}, 1: { foo: 'bar', x: 42 }, 2: { foo: 'baz', y: 13 } }

上記の例では、スプレッド構文は期待通りに動作しません。残りの引数があるため、引数の配列がオブジェクトリテラルにとして展開されます。

反復可能オブジェクトにのみ利用可能

オブジェクト自体は反復可能ではありませんが、配列の中で使用したり、 map(), reduce(), assign() などの反復関数と共に使用したりすることで反復可能になります。 2 つのオブジェクトをスプレッド演算子で結合する場合は、結合時に別の反復処理関数を使用することを前提としています。

スプレッド構文 (スプレッドプロパティの場合を除く) は、反復可能オブジェクトにのみ適用できます。

const obj = {'key1': 'value1'};
const array = [...obj]; // TypeError: obj is not iterable

大量の値を展開する場合

JavaScript エンジンには、引数の個数に上限があります。関数呼び出しでのスプレッド構文では、引数の個数がその上限を超えてしまう可能性に留意してください。詳細は apply() のページを参照してください。

仕様書

仕様書
ECMAScript (ECMA-262)
Array initializer の定義
ECMAScript (ECMA-262)
Object initializer の定義

ブラウザーの互換性

Update compatibility data on GitHub
デスクトップモバイルサーバー
ChromeEdgeFirefoxInternet ExplorerOperaSafariAndroid webviewAndroid 版 ChromeAndroid 版 FirefoxAndroid 版 OperaiOSのSafariSamsung InternetNode.js
Spread in array literalsChrome 完全対応 46Edge 完全対応 12Firefox 完全対応 16IE 未対応 なしOpera 完全対応 37Safari 完全対応 8WebView Android 完全対応 46Chrome Android 完全対応 46Firefox Android 完全対応 16Opera Android 完全対応 37Safari iOS 完全対応 8Samsung Internet Android 完全対応 5.0nodejs 完全対応 5.0.0
完全対応 5.0.0
完全対応 4.0.0
無効
無効 From version 4.0.0: this feature is behind the --harmony runtime flag.
Spread in function callsChrome 完全対応 46Edge 完全対応 12Firefox 完全対応 27IE 未対応 なしOpera 完全対応 37Safari 完全対応 8WebView Android 完全対応 46Chrome Android 完全対応 46Firefox Android 完全対応 27Opera Android 完全対応 37Safari iOS 完全対応 8Samsung Internet Android 完全対応 5.0nodejs 完全対応 5.0.0
完全対応 5.0.0
完全対応 4.0.0
無効
無効 From version 4.0.0: this feature is behind the --harmony runtime flag.
Spread in destructuringChrome 完全対応 49Edge 完全対応 79Firefox 完全対応 34IE 未対応 なしOpera 完全対応 37Safari 完全対応 10WebView Android 完全対応 49Chrome Android 完全対応 49Firefox Android 完全対応 34Opera Android 完全対応 37Safari iOS 完全対応 10Samsung Internet Android 完全対応 5.0nodejs 完全対応 6.0.0
Spread in object literals
実験的
Chrome 完全対応 60Edge 完全対応 79Firefox 完全対応 55IE 未対応 なしOpera 完全対応 47Safari 完全対応 11.1WebView Android 完全対応 60Chrome Android 完全対応 60Firefox Android 完全対応 55Opera Android 完全対応 44Safari iOS 完全対応 11.3Samsung Internet Android 完全対応 8.2nodejs 完全対応 8.3.0
完全対応 8.3.0
完全対応 8.0.0
無効
無効 From version 8.0.0: this feature is behind the --harmony runtime flag.

凡例

完全対応  
完全対応
未対応  
未対応
実験的。動作が変更される可能性があります。
実験的。動作が変更される可能性があります。
ユーザーが明示的にこの機能を有効にしなければなりません。
ユーザーが明示的にこの機能を有効にしなければなりません。

関連情報