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

《 初回公開:2023/04/12 , 最終更新:未 》

【 目次 】

レスト構文

レスト構文については分割代入の記事で紹介した。
簡単に言うと分割代入時の配列やオブジェクトの余った部分をまとめて受け取る事ができる。
サンプルプログラムを再掲すると

const [a, b, ...rest] = [1, 2, 3, 4, 5];
// a= 1, b= 2, rest= [3, 4, 5]

オブジェクトの場合は

const { a, b, ...rest } = { a: 1, b: 2, c: 3, d: 4 };
// a= 1, b= 2, rest= {c: 3, d: 4}

レスト構文を関数の引数として用いたものは残余引数とも呼ばれる。

function func(a, b, ...rest) {
    console.log(`a = ${a}, b = ${b}, rest = ${rest}`);
}

func(1, 2, 3, 4, 5);
// a = 1, b = 2, rest = 3,4,5

スプレッド構文 - Spread syntax

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

配列へのスプレッド構文の適用

レスト構文と役割は異なるがレスト構文と似たような構文でスプレッド構文というのがあって、
配列の要素を分解して関数に複数の引数を渡すのに便利。

function func(a, b, c) {
    console.log(`a = ${a}, b = ${b}, c = ${c}`);
}

let array=[1,2,3];
func(...array);
// a = 1, b = 2, c = 3

...arrayの部分がスプレッド構文。

スプレッドは日本語で開くとか広げるという意味。
ここでは、配列やオブジェクトを展開するという意味かな。

対して、レストは休息という意味もあるがここでの意味は「残りの」という意味で、レスト構文は余った引数をまとめるという事かな。
スプレッド構文とレスト構文はみためは似ているが、意味合い,役割が異なる。

可変長引数をもつ関数やメソッドに配列の値を渡すのにも便利で。

// レスト構文を使って可変長(残余)引数をとる関数を定義
function sum(...theArgs) {
    let total = 0;
    for (const arg of theArgs) {
        total += arg;
    }
    return total;
}

// スプレッド構文で配列の要素を展開し関数の引数として渡す
const array = [1, 2, 3];
// ...array ⇒ 1, 2, 3 に展開される。 配列を展開する。
// sum(1, 2, 3)
console.log(sum(...array)); // 6

console.logメソッドも可変長引数をとるので。

console.log(...array);    // 1 2 3

スプレッド構文を使って展開したものをさらに配列に戻す事ができる。
これは配列のコピーの作成に利用できる。

// 配列のコピーを作成
let clonedArray = [...array]

console.log(JSON.stringify(clonedArray));    // [1,2,3]

配列を一旦、分解した後、それをもとに新たな配列を組み立てたりさらにまた分解したりしてと無限に自由自在に配列を組み合わせる事ができる。

let array = ["a", "b", "c"];
...array ⇒ 配列を展開する。"a", "b", "c" に展開される。
[...array] ⇒ 配列["a", "b", "c"]に戻す。

console.log(JSON.stringify([...[...[...["a", "b", "c"]]]]));    // ["a","b","c"]

スプレッド構文と配列を組み合わせて新たな配列を合成できる。
配列のマージといったところかな。

const array2 = ["a", "b", "c"];
const array3 = [...array2, 0, ...array0, 4, 5];

console.log(JSON.stringify(array3));    // ["a","b","c",0,1,2,3,4,5]

中間の変数を使わずに直接配列を合成すると

const array3 = [...["a", "b", "c"], 0, ...[1, 2, 3], 4, 5];
console.log(JSON.stringify(array3));    // ["a","b","c",0,1,2,3,4,5]

レスト構文と組み合わせる事でさらに柔軟に配列を合成,展開できる。
こうなるとスプレッド構文もレスト構文も見分けがつかないような。

const [, ...rest_array] = [1, 2, 3];
console.log(...["a", ...rest_array, "b"]);    // a 2 3 b

レスト構文は反復可能(イテラブル)なオブジェクトに対しても適用可能で

let iterableObj = new Set()
iterableObj.add(1)           // Set [ 1 ]
iterableObj.add(5)           // Set [ 1, 5 ]

let array = [...iterableObj, '4', 'five', 6]; // iterableObj のすべての要素を挿入することで、2 つの配列を組み合わせる
console.log(JSON.stringify(array)); // [1,5,"4","five",6]

文字列も反復可能オブジェクトなので文字を1文字づつ取り出す事もできる。

console.log(JSON.stringify([..."あいうえお"]));  // ["あ","い","う","え","お"]

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

配列と同様、オブジェクトの要素(プロパティ)もスプレッド構文を使って展開できる。

オブジェクトのコピーを作成。

const obj1 = {
    name: "愚鈍人",
    age: 99
}

// オブジェクトのコピーを作成
let clonedObj = { ...obj1 };
console.log(JSON.stringify(clonedObj))  // {"name":"愚鈍人","age":99}

オブジェクトのコピーにはO、bject.assignメソッドを使う方法もあるがこちらの方が簡単。

obj1の要素をもとに新しい要素を追加したオブジェクトobj2を作成

const obj2 = {
    ...obj1,
    Gender: "Male"
}
console.log(JSON.stringify(obj2)); // {"name":"愚鈍人","age":99,"Gender":"Male"}

同名のプロパティが存在した場合は後から追加されたプロパティに上書きされるようだ。

// オブジェクトobj2のコピーを作成しage要素の値を差し替える
const obj3 = {
    ...obj2,
    age: "xxx"
}
console.log(JSON.stringify(obj3));  // {"name":"愚鈍人","age":"xxx","Gender":"Male"}

オブジェクトのマージ。

let otherObj = {
    name: "otherObj",
    address: "埼玉県行田市",
    email: "other@yahoo.co.jp"
}
let mergedObj = { ...obj2, ...otherObj }
// 同名のプロパティは後から追加されたプロパティが優先されて。
console.log(JSON.stringify(mergedObj))
// {"name":"otherObj","age":99,"Gender":"Male","address":"埼玉県行田市","email":"other@yahoo.co.jp"}

レスト構文とスプレッド構文を組み合わせて

// obj1からnameプロパティを除いた残りのプロパティと
let { name, ...restObj } = obj1;
// otherObjからnameプロパティとaddressプロパティを除いた残りのプロパティを
let { name: name2, address, ...restOtherObj } = otherObj;
// マージしたオブジェクトを作成
let mergeRestdObj = { ...restObj, ...restOtherObj };

console.log(JSON.stringify(mergeRestdObj)); // {"age":99,"email":"other@yahoo.co.jp"}

オブジェクトへのスプレッド構文はクラスのインスタンスにも適用できて

// PersonクラスのインスタンスのプロパティをObjectクラス(同一の要素っを持つObjectクラスのインスタンスに)に置き換える。
class Person {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
}
let person = new Person("愚鈍人", 99);
let personObj = { ...person };

console.log(`${person.constructor.name}クラス: ${JSON.stringify(person)}`)
// Personクラス: {"name":"愚鈍人","age":99}
console.log(`${personObj.constructor.name}クラス: ${JSON.stringify(personObj)}`)
// Objectクラス: {"name":"愚鈍人","age":99}
ページのトップへ戻る