MENU

JavaScript構文変数

【JavaScript】 コード中の「...」は意味があった スプレッド/レスト構文

更新日:2020/02/29

 

JavaScriptは本当に不思議ちゃんな言語である。
コード中で「...」とか何か考え込んでいたりする。

 

そうじゃなかった。
「...」には意味があった。

 

今回はJavaScriptのまじめな「...」を使った構文についてお伝えします。

 

 

■お願い
去年ECMAScript2020を頑張って日本語訳しましたが、誰も見てくれません・・・
誰かみて!!
【JavaScript】 学習のためECMAScript2020を日本語訳してみました

スプレッド構文とレスト構文

 

「...」は、オブジェクトを展開、または変数やリテラルを羅列したものを一つにまとめてくれる演算子です。

 

使用する目的によってスプレッドとレストの二つの構文に分かれます。

 

■スプレッド構文

 

スプレッド構文は配列などのオブジェクトを展開します。

 

[1 , 2, "aaa", "bbb"]  ― 展開 →  1 , 2, "aaa", "bbb"

 

■レスト構文(レストパラメーター)

 

レスト構文は、変数やリテラルの羅列を一つに集約します。

 

1 , 2, "aaa", "bbb"  ― 集約 →  [ 1 , 2, "aaa", "bbb"]

 

次から少し詳しく解説するので、ここではイメージだけ抑えておいてください。

スプレッド構文

 

スプレッド構文は配列などのオブジェクトを展開します。
まずは簡単な使い方を見ていきましょう。

 

関数呼び出し

 

スプレッド構文は関数呼び出しで利用できます。

 

スプレッド構文:関数呼び出し

function a( x , y , z ) {
        console.log( x , y , z );  // 結果: 1 2 3
}
let b = [ 1 , 2 , 3];
a(...b);

 

3つの引数を持つ関数aがあります。
この関数に、スプレッド構文で配列を展開して引数として渡しています。

 

スプレッド演算子を使用しない場合、次のようになると思います。

 

スプレッド構文を使用しない その1

a( b[0] , b[1] , b[2] );

 

配列の順番通りに渡したいとき、一つ一つ引数を書いていくのは面倒です。
スプレッド構文の方が楽ですね。

 

引数のリストに対して配列を使用できる、apply()メソッドがあります。

 

スプレッド構文を使用しない その1

a.apply( null , b );

 

こちらの方法でも、いいかもしれませんね。

 

ですがapply()は、newで使用できないという欠点があります。

 

newでapply()を使用

let a = function( x, y, z ){ this.x = x ; this.y = y ; this.z = z; };
let b = new a.apply( null, [ 1 , 2 , 3 ] );  // エラー:a.apply is not a constructor

 

実行すると、コンストラクターではないと言われて止まってしまいます。

 

参考:【JavaScript】 コンストラクターとは?関数とは違うのか?
参考:【JavaScript】 そろそろcall()とapply()を理解してみようと思う

 

newでスプレッド構文を使用

let b = new a( ...[ 1 , 2 , 3 ] );

 

スプレッド構文を使用すれば、エラーになりません。
便利ですね!

 

 

配列に展開

 

配列の初期化にもスプレッド構文を使用できます。

 

配列の初期化にスプレッド構文を使用

let a = [ 1 , 2 , 3 ];
let b = new Array(...a);    // b: Array(3)  [ 1 , 2 , 3 ]
let c = [ ...a ];       //  c: Array(3) [ 1 , 2 , 3 ]

 

例えば3行目のコードは、次のように展開されます。

 

let c = [ 1 , 2 , 3 ];

 

こう書くと、新しい配列が作成されているのがわかりますね。
同じものを作っているだけで意味がないと感じるかもしれません。
ですが配列を手軽に複製できるというのは、大きなメリットです。

 

多次元配列の展開

 

ただし多次元配列は完全には複製できません。

 

多次元配列にスプレッド構文を使用

let a = [ 1 , [ 2 , 3 ]  ];
let c = [ ...a ];
c[0] = 10;
c[1][0] = 20;
console.log( a );  //   Array [ 1 , [ 20 , 3] ]
console.log( c );  //   Array [ 10 , [ 20 , 3] ]

 

2次元配列に代入した値が、複製元にも影響を与えています。
これは配列への参照が複製されたためです。

 

多次元配列をスプレッド構文で複製するには、次のように配列の中身に対してもスプレッド構文を適用します。

 

多次元配列をスプレッド構文で完全コピー

function ArrayCopy( arry ){
    let length = arry.length;
    for( let i = 0 ; i < length ; i ++){
        if( Array.isArray( arry[ i ] ) ) {
                arry[i] = [ ...arry[ i ] ];
                ArrayCopy( arry[ i ] );
        }
    }
}
let a = [ 1 , [ 2 , 3 ] ];
let c = [...a ];
ArrayCopy( c );

 

c[ 0 ] = 10;
c[ 1 ][ 0 ] = 20;
console.log( a );   // Array [ 1 , [ 2 , 3 ] ]
console.log( c );   // Array [ 10 , [ 20 , 3 ] ]

 

うまくできました!!
ただし、配列の要素にオブジェクトが使用されているケースは考慮していません。

 

配列の結合・合成

 

また、手軽に配列を結合・合成できます。

 

配列の結合にスプレッド構文を使用

let a = [ 1 , 2 , 3 ];
let b = [ 4 , 5 , 6 ];
let c = [...a , "text1" , "text2" , ...b]; // Array(8) [ 1, 2, 3, "text1", "text2", 4, 5, 6 ]

 

concat()などの配列を結合するメソッドがありますが、スプレッド構文の方が直感的でいいですね。

 

オブジェクトを展開

 

スプレッド構文は、オブジェクトの展開もできます。

 

オブジェクトの展開にスプレッド構文を使用

let a = { x:1 , y:2 };
let b = { z:3 };
let c = { ...a , ...b };
console.log(c);  // Object { x: 1, y: 2, z: 3 }

 

配列と同じように、結合もできていますね。
ただし同じキーがあると、後から出てきた値で上書きされます。

 

オブジェクトの展開にスプレッド構文を使用

let a = { x:1 , y:2 };
let b = { y:3 };
let c = { ...a , ...b };
console.log(c);  // Object { x: 1, y: 3 }

 

分割代入で使用

 

分割代入にもスプレッド構文を使用できます。

 

分割代入でスプレッド構文を使用

let a = [ 1 , 2 ];
let [x , y ,z] = [ ...a , 3];
console.log(x,y,z);   //  1 2 3

 

後で解説するレスト構文を併用すると、配列を組み替えることができておもしろいです。

 

分割代入でスプレッド構文とレスト構文を使用

let a = [ 1 , 2 ];
let [x , ...b ] = [ ...a , 3 ];
console.log( x , b );    //  1  array [ 2 , 3]

 

参考:【JavaScript】 分割代入はどこが便利なのか

スプレッド構文とイテラブルなオブジェクト

 

ここまで主に配列に対してスプレッド構文を使用する例を、お伝えしてきました。

 

実際にはスプレッド構文は、イテラブルなオブジェクトで使用します。
(オブジェクトの展開は除きます)

 

イテラブルなオブジェクトとは値を反復して返すことができるオブジェクトで、配列もその一つです。
参考:【JavaScript】 Iterator(イテレーター)とは?避けて通りたいけど説明してみる

 

 

次のように自作のオブジェクトをイテラブルなオブジェクトとして構築すると、スプレッド構文を利用できます。

 

イテラブルなオブジェクトとスプレッド構文

let a = {
    x:1 ,
    y:2 ,
    [Symbol.iterator] :  function* (){
            yield this.x;
            yield this.y;
        }
};
let b = [ ...a ];
console.log( b );  // Array [ 1 , 2 ]

 

使い道は…考えてみてください!

レスト構文

 

レスト構文(レストパラメータ)は残余引数とも呼ばれていて、変数やリテラルの羅列を一つの配列に集約します。

 

関数呼び出しで使用

 

関数に引き渡された引数で、残った全てを配列にセットします。

 

function a( type , ...param ){

 

    switch( type ){
        case 1:
                    // param[]から3つ参照
                    break;
        case 2:
                    // param[]から5つ参照
                    break;
    }
}
a( 1 , data1 , data2 , data3 );
a( 2 , data1 , data2 , data3 , data4 , data5 );

 

上の例のような、可変長引数を実現するのに便利ですね。

 

分割代入で使用

 

分割代入にもレスト構文を使用できます。

 

分割代入でレスト構文を使用

let [x , ...y ] = [ 1 , 2, 3 ];
console.log(x,y);   //  1 [ 2 ,3 ]

 

参考:【JavaScript】 分割代入はどこが便利なのか

まとめ

 

配列が絡む処理で、スプレッドとレストの構文は便利に使えそうですね。

 

僕もこれからは積極的に使っていこうと思います。

記事の内容について

 

こんにちはけーちゃんです。
説明するのって難しいですね。


「なんか言ってることおかしくない?」
たぶん、こんなご意見あると思います。

裏付けを取りながら記事を作成していますが、僕の勘違いだったり、そもそも情報源の内容が間違えていたりで、正確でないことが多いと思います。

そんなときは、ご意見もらえたら嬉しいです。

ご意見はこちら。
https://note.affi-sapo-sv.com/info.php

【お願い】

お願い

■このページのURL


■このページのタイトル


■リンクタグ