🌽

【JavaScript】スプレッド構文を理解する

4 min read

スプレッド構文って知ってますか..?

いきなりですが、JavaScriptの「スプレッド構文」って知っていますか?
変数名の前に...をつけるアレです。最近ReactでReduxの勉強をしていたときに、急に...payloadみたいな記法が現れて、なんだこれ...??と困惑しました。

ちょろっとgoogle先生に聞いてみました。
「js ... 構文」 🔍
すると以下の記事に目がつきました。(さすがmozillaさん、ありがとうございます🙇‍♂️)

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Spread_syntax

...を使用した記法をスプレッド構文というみたいです。

というような感じで、初めて聞いたスプレッド構文について勉強したので、まとめていこうと思います。

スプレッド構文とは...?

スプレッド構文とは、以下のような説明がされていました。

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

展開と言われても正直イメージがつかないと思います。実際にコードを書いて説明していきます。

関数の引数でスプレッド構文を使用する

1番簡単な例として、関数の引数でスプレッド構文を使用する場合を考えてみます。
以下のようなコードになります。

function sum(x, y, z) {
    return x + y + z;
}

const numbers = [1, 2, 3];
const result = sum(...numbers);

console.log(result);  // 6

なんと、[1, 2, 3] → 1, 2, 3 と展開され、引数に渡すことが可能になりました。

配列でスプレッド構文を使用する

次に、配列でスプレッド構文を使用する場合を考えてみます。

既存の配列を一部として使用して新しい配列を作成するには、push()splice()concat() などを組み合わせる必要があります。
しかし、スプレッド構文を使用すると、すごく簡潔に記述できます。

  • 既存の配列を一部として使用して新しい配列を作成
const array = ['appple', 'orange'];
const newArray = ['melon', ...array, 'banana', 'peach'];

console.log(newArray);  // ['melon', 'appple', 'orange', 'banana', 'peach']
  • 配列の複製を行う例
const array = ['appple', 'orange'];
const newArray = [...array];
newArray.push('banana');

console.log(array);     // ['appple', 'orange']
console.log(newArray);  // ['appple', 'orange', 'banana']

この時の複製はいわゆる深いコピーであり、異なる参照を持つ配列が生成されます。そのため、newArrayに要素を追加しても、コピー元のarrayには何ら影響はありません。

  • 配列の結合を行う例
let array = [0, 1, 2];
const array2 = [3, 4, 5];

array = [...array, ...array2];
console.log(array);  // [0, 1, 2, 3, 4, 5]

一般的に配列の結合はconcat()を使用しますが、上記のようにスプレッド構文でも記述できます。

オブジェクトでスプレッド構文を使用する

次に、オブジェクトでスプレッド構文を使用する場合を考えてみます。

const dict = { 'name': 'nobita', 'age': 11 };
const dict2 = { 'name': 'doraemon', 'age': 20 };
const dict3 = { 'tool': 'dokodemodoor' };

const clonedObj = { ...dict };
console.log(clonedObj);  // { name: 'nobita', age: 11 }

const mergedObj = { ...dict, ...dict2 };
console.log(mergedObj);  // { name: 'doraemon', age: 20 }

const mergedObj2 = { ...dict, ...dict3 };
console.log(mergedObj2);  // { name: 'nobita', age: 11, tool: 'dokodemodoor' }

上記の例のように、オブジェクトも配列同様に、複製やマージが可能です。
マージする際にプロパティ名が被った場合は、後に指定したオブジェクトの値で上書きされます。

React + Redux におけるスプレッド構文の使用

最後に自分が直面した React + Redux におけるスプレッド構文の使用を説明します。
ReactとReduxを知らない人はすっ飛ばして大丈夫です。ReactとReduxを知ってる方向けへの発信です。

Reducerとは、storeで管理しているstateの値を更新する役割を持ちます。actiontypeプロパティに応じて、stateの更新処理を定義します。

reducers.js
import initialState from '../store/initialState'
import * as Actions from './actions'

export const UserReducer = (state = initialState.users, action) => {
    switch (action.type) {
        case Actions.SIGN_IN:
            return {
                ...state,
                ...action.payload
            }
        default:
            return state
    }
}
initialState.js
const initialState = {
    users: {
        isSignedIn: false,
        role: '',
        uid: '',
        userName: ''
    }
};

export default initialState
actions.js
export const SIGN_IN = 'SIGN_IN';

export const signInAction = (userState) => {
    return {
        type: 'SIGN_IN',
        payload: {
            isSignedIn: true,
            role: userState.role,
            uid: userState.uid,
            userName: userState.userName
        }
    }
};

signInActionの返り値であるオブジェクトをreducersでより簡潔に記述するため、またプロパティが失われないようにするためにスプレッド構文を使ってマージしていたことがわかりました。

まとめ

今回はスプレッド構文について、紹介しました。初めて聞いた記法だったのですが、勉強していく上で便利なものだと気付かされました。とても勉強になりました。