はじめに
先日スプレッド構文を用いて配列を扱う学習を行っていた際に気がついたことがありましたので、書き残しておきたいと思います。
スプレッドした配列を変数に入れる、つまり配列のコピーを作ってから配列メソッドを実行する場合
と
スプレッドした配列自体に配列メソッドを実行する
のではどのような違いがあるのかについて調べました。
配列をコピーし、変数に格納した後、変数に対して要素を追加する
const originalArr = [0,1,2,3,4,5];
const newArr = [...originalArr];
newArr.push(6);//元の配列をコピーし、全く別の配列として定義した後、作成した配列に対して追加メソッドを実行
console.log(newArr)//[0,1,2,3,4,5,6]
これは想定通りの挙動をしていると思います。
僕が問題にぶち当たったのは、次のパターンです。
スプレッドした配列そのものに対して追加メソッドを実行
const originalArr = [0,1,2,3,4,5];
const newArr = [...originalArr].push(6));//スプレッドした配列に対してpush
console.log(newArr)//[7];//push()の返り値である配列lengthが代入されている
???
と思ったのですが、@dswc 様からご教授をいただきました。ありがとうございます。
なぜ異なる結果になるのか?
上述のコード:元の配列をスプレッドし、展開された配列に対してメソッドを実行する動作を一度に行うコードを日本語に直すと
const 変数 = [...配列を展開する動作].展開した配列に対するメソッド();
となります。
ここにおいて変数には、配列メソッドが実行された元の配列ではなく、元の配列に対して実行された配列メソッドの戻り値が代入されているのです。
今回で言えば、Array.push()
はメソッドが実行された配列のlengthを返すので、変数にはorginalArr
のlength、つまり7
が代入されます。
const originalArr = [0,1,2,3,4,5];
const newArr = [...originalArr].push(6));//push()メソッドの戻り値である配列のlengthが代入
console.log(newArr)//7
試しに、push()の引数を指定せずに実行すると、
const originalArr = [0,1,2,3,4,5];
const newArr = [...originalArr].push();
console.log(newArr)//6 ←配列の長さに変化なし!
となり、実行元の配列の長さが代入されています。
普段配列メソッドを使う時、実行した配列にしか注目してこなかったため、今回のように配列メソッドの戻り値を意識したことがなく、大変驚きました。
改めて、ご指摘いただきありがとうございます!
おまけ: スプレッドした配列自体に対して実行された配列メソッドの効果はどうなっているの??
今回のトピックとは関係ないですが、配列のスプレッドと配列メソッドを一度に実行した場合、元の配列はどうなっているのでしょうか。
つまり、
const originalArr = [0,1,2,3,4,5];
const newArr = [...originalArr].push(6);
こうした場合、originalArr
はどのようになっているのでしょうか。私は、先述したとおり、push()メソッドの戻り値が6→7変化していたことからoriginalArr
には新しい値が追加されていると思っていたのですが、実際には:
console.log(orignalArr)//[0,1,2,3,4,5]
なんと、originalArrの中身は変化していませんでした。
つまり、スプレッド構文を用いて展開した配列を実行元とした配列メソッドには効果がないのですね。
push以外にspliceメソッドも実行してみましたが、同様でした。
あくまで配列メソッドの戻り値だけが変化して返ってきているみたいなので、気をつけたほうが良さそうです。
結論:配列を操作するなら、一旦元の配列のコピーを作成してからにすべし
immutableな処理を行うためにも、
1:元の配列をスプレッドしてコピーを作成
2:コピーに対して配列操作をしてあげる
が無難なのかな・・・と思います。