JavaScript の in の 闇
さて、あなたには この画像 が理解できますか?
in
は何をしているのか?
そもそも x in array
なので、配列(array)に対して指定の値が含まれている・・・と考えがちですが実は違います
in 演算子は、指定されたプロパティが指定されたオブジェクトにある場合に true を返します。
指定されたプロパティ、なのでキーが存在しているかどうかを確認するのがJavaScriptのin
演算子です。
なので・・・
let l = [1, 2, 3, 4]
↓
{
0: 1,
1: 2,
2: 3,
3: 4
}
このようにみると、0
と"0"
でtrue
が返り、4
でfalse
が返される理由がわかりますね。
false in [1, 2, 3, 4]
は何を返すのか?
0
でも"0"
でもin
のoperatorが使用できることがわかりました。
ここではStrictモードによる比較はされていないようなので、false
で検証してみるといかがでしょうか?
let l = [1, 2, 3, 4]
console.log(false in l)
// false
.
.
.
( ^ω^)はて?
想像していた結果と異なりました。
厳格な比較はされていないようですが、==
による比較でもなさそうです・・・
なので、ECMAScriptの仕様書を追いかけてみましょう。
追跡 ECMAScript
in 演算子(ECMAScript)
13.10 Relational Operators
13.10.1 Runtime Semantics: Evaluation
RelationalExpression : RelationalExpression in ShiftExpression
1. Let lref be ? Evaluation of RelationalExpression.
2. Let lval be ? GetValue(lref).
3. Let rref be ? Evaluation of ShiftExpression.
4. Let rval be ? GetValue(rref).
5. If rval is not an Object, throw a TypeError exception.
6. Return ? HasProperty(rval, ? ToPropertyKey(lval)).
6番の返り値部分にフォーカスして見てみましょう。
HasProperty
は、単純にプロパティが存在しているかどうかを検証しているようです。
第1引数のrval
(right value)は、名称からinの右側・・・オブジェクトまたは配列が該当します。
第2引数のlval
(left value)は、名称からinの右側・・・存在するかどうかを確認しているキーの名称が該当します。
lval
が ToPropertyKey
というメソッドに渡されているので、次はこちらを追いかけてみましょう。
ToPropertyKey(ECMAScript)
1. Let key be ? ToPrimitive(argument, STRING).
2. If key is a Symbol, then
a. Return key.
3. Return ! ToString(key).
3番の返り値部分にフォーカスして見てみましょう。
最終的にはToString
に掛けられたキーが返却されているようです。
ここにfalse
が渡った場合のケースを見てみましょう。
ToString(ECMAScript)
1. If argument is a String, return argument.
2. If argument is a Symbol, throw a TypeError exception.
3. If argument is undefined, return "undefined".
4. If argument is null, return "null".
5. If argument is true, return "true".
6. If argument is false, return "false".
7. If argument is a Number, return Number::toString(argument, 10).
8. If argument is a BigInt, return BigInt::toString(argument, 10).
9. Assert: argument is an Object.
10. Let primValue be ? ToPrimitive(argument, STRING).
11. Assert: primValue is not an Object.
12. Return ? ToString(primValue).
6番を見ると、false
が渡された際には"false"
が返されていることがわかります。
この結果が最終的にHasProperty
の第2引数に渡されます。
false in [1, 2, 3, 4]
の結論
array["false"] が 存在するかを検証している
例:左 in 右
- 左が
0
の場合 -> array["0"] が存在するか - 左が
"0"
の場合 -> array["0"] が存在するか - 左が
false
の場合 -> array["false"] が存在するか
結論
不思議な挙動も、仕様を読み解いていくことで色んなことがわかりますね( ^ω^)
Discussion