0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

JavaScriptの Object が子供な Array を fill で作ろうとして詰まった話

Posted at

経緯

JavaScriptの配列にオブジェクトをもたせて、なおかつそれぞれ己の親配列内でのインデックスがあると実装が楽なんで要素として持たせようという場面。

次のようにfillを使用してデフォルト値p=0を与えつつ、mapのcallbackの第2引数のindexをqにセットしようとした。
が、すべてのqが2になってしまった。

x = Array(3).fill({p:0}).map((v,i)=>{v.q=i;return v;})

// 結果
[
  { p:0, q=2 },
  { p:0, q=2 },
  { p:0, q=2 }
]

結論

fill関数に渡されたオブジェクトは複製されない。

解説

まあ結論読めばあとは蛇足なんですが、
fill関数は渡された値を各要素値として直接代入するような操作をする。
渡されているのがObjectへの参照である場合、そのひとつの参照を配列の全要素にブチこむため、
せっかく配列なのに見ているObjectは一個という悲しい状態になる。

よって、mapでv.q=i;を呼ばれるたびに、同じObjectのqを更新していた。

v.q=0; // x[0].q = 0; x[1].q = 0; x[2].q = 0;
v.q=1; // x[0].q = 1; x[1].q = 1; x[2].q = 1;
v.q=2; // x[0].q = 2; x[1].q = 2; x[2].q = 2;

回避策

僕がArray(3).fill()を呼ぶのは、
もともとは配列要素の値がemptyのような状態1だとmapが動かないからであって、
デフォルト値を渡すためではなかった。

おとなしくundefinedで埋めてやれば return とかも書かなくて済む

x = Array(3).fill().map((v,i)=>({p:0,q:i}))

まあ、デフォルト値を明示的に分離したいお気持ち表明をしてしまったせいで躓いたというクソつまんない原因でしたとさ。

  1. これもちと謎々なのだが、Array(3).map((v,i)=>i)[0,1]になってくれない。Array(3)はコンソールでは[empty x 3]と表記され、このemptyに該当する要素は処理から除外される。MDNのページには、「配列の中で存在しない要素(設定されたことがない添字・削除された要素・値を割り当てられたことがない要素)に対しては呼び出しません。」と記載がある。正直意味がわからんし処理しろと思うが、この仕様が活きている場所もあるかもしれないので仕方がない。該当するかどうかはObject.keys(Array(2)) // result: []のようにするとテストできるはずだ。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?