JavaScript: f( array ) よりも f( [...array] ) がいいとき?


単純なことなんですが、最近うっかり踏みそうになったプチ地雷からのクイズ?です。

突然ですが

何が表示されるでしょうか?

const array2Gen = xs =>
  function*(){ yield* xs; }();

const a = [0,1,2];
const g =array2Gen( a );

for(let i of a.keys() ){
  a[i] = undefined;
  console.log(g.next().value);
};

配列をジェネレータにして、順番に配列の要素の値を変更しながらジェネレータを呼んで表示させるということです。
こんなこと普通はしないでしょうけど。

答は:

undefined
undefined
undefined

です。
const で配列を宣言しても、配列の要素は変更できるのでしたね。ある意味、当然の結果です。
でも場合によっては、ジェネレータにした時点で中味が固定されて、もう変ってほしくないと思うんじゃないでしょうか?

0 1 2 が返ってきてほしいなあ...

うけとった配列をコピーして使う

これはどうでしょう。何が表示されるでしょうか?

const array2Gen = xs =>
  function*(){
    const ys = [...xs];
    yield* ys; 
  }();

const a = [0,1,2];
const g =array2Gen( a );

for(let i of a.keys() ){
  a[i] = undefined;
  console.log(g.next().value);
};

受け取った配列 xs を[...xs]で浅いコピーをしてからジェネレータにしています。
これで xs が変更されても大丈夫なはずです。
が、実行してみると...

undefined
1
2

あれ?一回目だけ変更されてる。なぜでしょう?
説明できます?
ともかく、このやりかたでは不十分です。別のやりかたを考えましょう。

コピーしてから渡す

今度はどうでしょう。何が表示されるでしょうか?

const array2Gen = xs =>
  function*(){ yield* xs; }();

const a = [0,1,2];
const g = array2Gen( [...a] );

for(let i of a.keys() ){
  a[i] = undefined;
  console.log(g.next().value);
};

今度は const g = array2Gen( [...a] );で浅いコピーをしてから関数に渡しています。
結果は?:

0
1
2

やったー!やっとできたー

まとめ

  1. 何でできたんだろう? 説明できる?
  2. いつもコピーしたほうがいい? どんなときにコピーするべき?
  3. 配列だけ? 他に気をつけたほうがいいモノは?
  4. 浅いコピーで大丈夫? どうやって見分ける?
  5. やった方がいいことは? 逆にやらない方がいいことは?
  6. ほかに気付いたこと? 何でも

...というようなクイズ? なんですが、何かお役に立つ内容でしたでしょうか?