ぺんぎんらぼ

お笑いとマンガ好きなしょぼしょぼWeb系エンジニアの日記です。たまに絵を描きます。

お笑いとマンガ好きなしょぼしょぼWeb系エンジニアの日記です

JavaScriptのArray.fill()の注意点

JavaScriptで少し「あれ?」となったことを共有です。 多次元配列の操作中に、思っていたのと違う挙動になってしまいました。

間違った例

コード

const loopArray = new Array(3).fill(new Array(2)); // 初期化

for (let y = 0; y < 3; y++) {
  for (let x = 0; x < 2; x++) {
    loopArray[y][x] = "[y]=" + y + ",[x}=" + x; // 値の設定
  }
}

for (let y = 0; y < 3; y++) {
  for (let x = 0; x < 2; x++) {
    console.log(loopArray[y][x]); // 値の出力
  }
}

出力結果

"[y]=2,[x}=0"
"[y]=2,[x}=1"
"[y]=2,[x}=0"
"[y]=2,[x}=1"
"[y]=2,[x}=0"
"[y]=2,[x}=1"

期待していた出力結果

"[y]=0,[x}=0"
"[y]=0,[x}=1"
"[y]=1,[x}=0"
"[y]=1,[x}=1"
"[y]=2,[x}=0"
"[y]=2,[x}=1"

修正後

期待値のように出力するには、コードを次の★のように直します。

const loopArray = new Array(3); // 初期化★
//const loopArray = new Array(3).fill(new Array(2)); 

for (let y = 0; y < 3; y++) {
  loopArray[y] = new Array(2); // 追加★
  for (let x = 0; x < 2; x++) {
    loopArray[y][x] = "[y]=" + y + ",[x}=" + x; // 値の設定
  }
}

for (let y = 0; y < 3; y++) {
  for (let x = 0; x < 2; x++) {
    console.log(loopArray[y][x]); // 値の出力
  }
}

解説

Array.fill()のコードコメントには、次のように書かれています。

/**
 * Changes all array elements from `start` to `end` index to a static `value` and returns the modified array
 * @param value value to fill array section with
 * @param start index to start filling the array at. If start is negative, it is treated as
 * length+start where length is the length of the array.
 * @param end index to stop filling the array at. If end is negative, it is treated as
 * length+end.
 */
fill(value: T, start?: number, end?: number): this;

一行目の「static value」というのがミソです。 fill()の引数として渡したnew Array(2)は、親の配列要素ごとにインスタンスが生成される訳ではなく、一つのstatic変数として扱われているようです。 配列のようにプリミティブではない値を渡した場合、各配列要素は同じ参照を見るようになってしまうということですね。