はじめに
ReactやTypeScriptを触ってるとよくでてくる「…hogehoge」というやつが何なのかちゃんと理解していなかったので知ったことをまとめる。
結論
同じ「…」であっても書かれている場所によって以下のどちらかの意味を持つ。
* スプレット構文
* レストパラメータ
以下、ひとつずつまとめ。
スプレット構文
以下の場所に書かれている「…」はスプレット構文である。
基本、…配列orオブジェクトで中身が展開されるものと捉えておけば良さげ。
パターン1: 関数呼び出しの引数パターン
const args = [10, 20, 30];
const myFunc = (x, y, z) => { return x + y + z };
console.log(myFunc(...args)); // ...args はスプレット構文
// 60
パターン2: 配列の中パターン
const someArray = [1, 2, 3];
const items = [...someArray]; // ...someArray はスプレット構文
console.log(items);
// Array [1, 2, 3]
パターン3: オブジェクトの中パターン
const someObject = {foo: 'good', bar: "luck", key: 3};
const obj = {...someObject}; // ...someObject はスプレット構文
console.log(obj);
// Object { foo: "good", bar: "luck", key: 3 }
単純に中身が展開されて便利!すてき!っていうだけではなく、スプレット構文で展開された配列は元の配列とは違う配列となる。
つまり参照関係がなく新しく複製されるのでイミュータブルにできてバグを防げそうな予感。
こういうこと。
var source = [10, 20, 30];
var copiedArray = [...source]; // これはイミュータブルに複製してくれるので
copiedArray.push(40);
console.log(copiedArray); // [10, 20, 30, 40] こっちは40が突っ込まれ
console.log(source); // [10, 20, 30] 元は変わらない
var source = [10, 20, 30];
var copiedArray = source; // 参照です
copiedArray.push(40);
console.log(copiedArray); // [10, 20, 30, 40]
console.log(source); // [10, 20, 30, 40] 同じ配列を参照しているだけなので
ただしスプレット構文では2階層目以降は複製されない(参照が残る)ので注意。
var source2 = [[1,2,3],[4,5,6],[7,8,9]];
var copied = [...source2];
copied.push([10]);
console.log(copied);
// 0: (3) [1, 2, 3]
// 1: (3) [4, 5, 6]
// 2: (3) [7, 8, 9]
// 3: [10]
console.log(source2);
// 0: (3) [1, 2, 3]
// 1: (3) [4, 5, 6]
// 2: (3) [7, 8, 9]
//// 一階層目までは影響受けてない
copied[0][0] = 999;
console.log(copied);
// 0: (3) [999, 2, 3]
// 1: (3) [4, 5, 6]
// 2: (3) [7, 8, 9]
// 3: [10]
console.log(source2);
// 0: (3) [999, 2, 3]
// 1: (3) [4, 5, 6]
// 2: (3) [7, 8, 9]
// 2階層目は参照が残っているのでこうなってる
これはオブジェクトでも同様の考え方となる。
レストパラメータ
関数自体の定義の引数部分に現れる「…」はレストパラメータ。
function someFunc(someparam, ...args) // ...args はレストパラメータ
{
// do something
}
レストパラメータはrest parameter(残り物のパラメータ)で「可変長引数」である。
someFunc(someparam, …args)だと、someparamが必須の引数で残りは何個あるかわからん、という引数をこういうふうに書ける。
const someFunc = (param, ...list) =>
{
list.forEach((l, ix) => console.log(ix + ": " + param + l));
}
someFunc('hello, ', 'gridman', 'gridknight', 'dynazenon');
// 0: hello, gridman
// 1: hello, gridknight
// 2: hello, dynazenon
配列やオブジェクトを展開する、というスプレット構文とは全然違っていてまさに言葉通り「可変長引数」を表している。
試しにここでヒーローたちを単純に配列に置き換えるとこんな風になる。
const heroes = ["gridman", "gridknight", "dynazenon"];
const someFunc = (param, ...list) =>
{
list.forEach((l, ix) => console.log(ix + ": " + param + l));
}
someFunc('hello, ', heroes);
// 0: hello, gridman,gridknight,dynazenon
このようにあくまで可変長配列であるレストパラメータにはheroesという一つの配列だけが渡るのでこうなる。
最後に、レストパラメータの非構造化について、ここに渡される配列をばらけさせることができる。
…listだったのを…[list]というふうに書くとそうなる。
const heroes = ["gridman", "gridknight", "dynazenon"];
const someFunc = (param, ...[list]) =>
{
list.forEach((l, ix) => console.log(ix + ": " + param + l));
}
someFunc('hello, ', heroes);
// 0: hello, gridman
// 1: hello, gridknight
// 2: hello, dynazenon
公式ドキュメント
ぜんぶここに書いてる。
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Spread_syntax
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Functions/rest_parameters
No responses yet