LoginSignup
2
0

More than 3 years have passed since last update.

JavaScriptの配列はクセが強い ~lengthを疑え~

Last updated at Posted at 2020-03-29

ブラウザの開発者ツールで以下を確認してみましょう。

最終的に6個の要素を持つ配列が出来そうなソース
a=[10]
a[5]=20
a
a.length

image.png

empty × 4 の部分が曲者で、実はこの部分には「要素は無い」。
a[1] を評価すると undefined になるので「aの1番目には undefined が入っている」かのように見えますが、正確には存在しないキーを指定されたから undefined になっただけです。

それが何?

さて、例えば終端が不確定な配列データをグラフ上にプロットしたいとかの要件で「配列の長さを最低N個まで拡張する」という関数を作る場合、何も考えずに実装するとこういうソースになると思います。

function fillUp(arr, length) {
  arr[length - 1] = arr[length - 1]; // 配列の長さがlength以上なら何も起こらず、未満ならundefinedが代入される事で配列の長さがlengthまで伸びる、ような気がする
  // a.length = Math.max(a.length, length) // こちらでも
}

冒頭のaを与えて fillUp(a, 10) を実行すると確かに a[9]undefined は入るし a.length も10になります。
が、lengthが10だからといって10回ループする事を期待して a.forEach() を実行しても実際には3回しかループしません。

fillUp(a, 10)
b=a.length
a.forEach(() => --b) // bはゼロになるはず
b

image.png

更に、「undefinedの要素を別の値に置き換えた配列を作りたい」といった処理も失敗します。

a2 = a.map(v => v === undefined ? 'empty' : v)
a2[1] // 'empty'に…ならない!

image.png

今aは具体的にはどういう状態か、以下で確認できます。

[...Array(a.length)].map((x,i) => ({index:i, exists: (i in a), value: a[i]}) )

image.png

おわかりいただけただろうか。

紛らわしいので何とかしたい

万能薬は無い気がします。どなたかご存知なら教えてください。

参照する側で気を付けるなら a.~ をやめて [...Array(a.length)].map((x,i) => a[i]).~ を使います。面倒臭い!

修正前
a.map(v => v === undefined ? 'empty' : v)
修正後
[...Array(a.length)].map((x,i) => a[i]).map(v => v === undefined ? 'empty' : v)

データを作る側で対処するなら、地道に埋めたてます。

修正前
function fillUp(arr, length) {
  arr[length - 1] = arr[length - 1];
}
修正後
function fillUp(arr, length) {
  [...Array(length)].forEach((x,i) => arr[i] = arr[i])
}

という事はpopも実際に代入されている値しか取り出さないのでは?

image.png

あ、そこはlength準拠の挙動なんですね…。

番外編 - これは添え字?

image.png

a.lengthは1から変わらず、 "2.1" というキーの独自プロパティが生えただけでした。 forEachmappop も2.1には反応しません。「だからどうした?」とか言わない。

2
0
2

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
2
0