3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

1人フロントエンドAdvent Calendar 2023

Day 23

JavaScript・TypeScriptにおけるfor (const key in x)に気をつけて!

Posted at

はじめに

JavaScript・TypeScriptにはオブジェクト、配列に対するさまざまな操作が提供されています。
この記事では中でもfor...inの扱う上で注意すべき点について解説します。

const object = {
  apple: 'red',
  banana: 'yellow',
  orange: 'orange',
};

for (cont key in object) {
  console.log(object, object[key]);
}
// apple red
// banana yellow
// orange orange

後ほど記述しますが、for...inは多くのスタイルガイドやリンターで非推奨となっています。利用するときは明確で固い意志を持ってください。

列挙可能な全ての値で反復する

for...inはオブジェクトのキーで反復処理を行う文法ですが、厳密にはオブジェクトのキーだけではなくプロトタイプなど列挙可能な全ての値で反復処理を行います。

const object = {
  apple: 'red',
  banana: 'yellow',
  orange: 'orange',
};

object.__proto__ = { grape: 'purple' };

for (cont key in object) {
  console.log(object, object[key]);
}
// apple red
// banana yellow
// orange orange
// grape undefined

オブジェクト自身のプロパティのみ処理する方法もあります。ES2022がサポートされていればObject.hasOwnを、サポートされていなければObject.prototype.hasOwnProperty.callを用いて判定すれば良いのです。

const object = {
  apple: 'red',
  banana: 'yellow',
  orange: 'orange',
};

object.__proto__ = { grape: 'purple' };

for (cont key in object) {
  if (Object.hasOwn(, key)) {
    console.log(key);
  }
}
// apple red
// banana yellow
// orange orange

for (const key in object) {
  if (Object.prototype.hasOwnProperty.call.hasOwn(, key)) {
    console.log(key);
  }
}
// apple red
// banana yellow
// orange orange

object.hasOwnPropertyではなく、わざわざObject.prototype.hasOwnProperty.callを用いたのはobjectprototypeが汚染されて、hasOwnPropertyがオーバーライドされていることを防ぐためです。

反復の順序が保証されない

for...inループを使用した反復処理の順序は必ずしもオブジェクトにプロパティが追加された順序とは一致しないです。

const object = {
  '2': 'two',
  '1': 'one',
  '3': 'three',
};

for (const key in object) {
  console.log(key);
}
// 1
// 2
// 3

この例では、プロパティのキーが整数であるため、キーが数値順に出力されます。
for...ofを利用することで、反復の順序をプロパティーが追加された順番できます。

const object = {
  '2': 'two',
  '1': 'one',
  '3': 'three',
};

for (const key of Object.keys(object)) {
  console.log(key);
}
// 2
// 1
// 3

出力順に依存した処理を書くときは注意してください。

配列

先ほどの話と同じような内容になりますが、配列に対してfor...inを利用するときは配列のインデックスが文字列で返されます(下記の例は文字列であることを強調してダブルクォーテーションで書いています)。

const arr = [4, 5];

for (const key in arr) {
  console.log(key, arr[key]);
}
// "0" 4
// "1" 5

for...inはキーが文字列であるとしているのでキーの型が常にstringになってしまいます。

以下のコードはキーが数値で定義されていますが、文中ではstringと推論されてしまいます。

const object = {
  1: 'one',
  2: 'two',
};

for (const key in object) {
  console.log(key);
}
// "1"
// "2"

推論だけじゃなく、実際の値も文字列として扱われることに注意してください。

正確に推論させたい場合はObject.keys()などを用いてキーを抽出してfor...ofで反復処理を行なってください。

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?