はじめに
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
を用いたのはobject
のprototype
が汚染されて、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
で反復処理を行なってください。