説明できない実用的なJavaScriptコードに出くわしました。例えば:
+[]===0
-[]===0
~[]===-1
~-~[]===-2
~-~-~-~-~[]===-5
~-~-~-~-~[]+~[]===-6
~+~[]===0
~+~+~[]===-1
~+~+~+~[]===0
これらの表現の論理を説明できますか?
説明できない実用的なJavaScriptコードに出くわしました。例えば:
+[]===0
-[]===0
~[]===-1
~-~[]===-2
~-~-~-~-~[]===-5
~-~-~-~-~[]+~[]===-6
~+~[]===0
~+~+~[]===-1
~+~+~+~[]===0
これらの表現の論理を説明できますか?
[]
は空の配列オブジェクトなので、次のようになります。
+[]: 空の配列を強制的に正の整数、つまり 0、つまり === から 0 にする
-[]: 空の配列を強制的に負の整数、つまり 0、つまり === から 0 にする
~[]: ビット単位の NOT -1 に評価される空の配列、つまり === から -1
~-~[]: 否定された NOT 化された空配列のビット単位の NOT:~-(-1) -> ~1 -> -2
等...
何かに+
or演算子を使用すると、それが呼び出されます。を返すので、最初の 2 つの答えが得られます。-
Number
Number([])
0
~
演算子はビット単位のNOT です。基本的に、数値のすべてのビットを反転し、 に変更0
し-1
ます。ビット演算子の詳細については、こちらを参照してください。
残りは、これらのケースの単なる組み合わせです。これらを組み合わせて、好きな数を作ることができます。
質問で示した結果を言い直すのではなく、その結果が得られた理由を説明しようと思います。
説明
最初の例だけを取り上げて (残りは同様のことを行うため)、仕様に従って何が起こるかを確認できます。
11.4.6 Unary + Operatorから、ToNumber
変換が行われていることがわかります。
ToNumber(GetValue(expr)) を返します。
9.3 ToNumberから、オブジェクト (配列など) が与えられた場合、ToPrimitive
変換が行われた後にToNumber
.
物体
次の手順を適用します。
- primValue を ToPrimitive(入力引数、ヒント番号) とします。
- ToNumber(primValue) を返します。
9.1 から Primitiveまで、オブジェクトを取得すると、そのオブジェクトがフェッチされToPrimitive
ていることがわかります。[[DefaultValue]]
物体
オブジェクトのデフォルト値を返します。オブジェクトのデフォルト値は、オブジェクトの [[DefaultValue]] 内部メソッドを呼び出し、オプションのヒント PreferredType を渡すことによって取得されます。[[DefaultValue]] 内部メソッドの動作は、8.12.8 のすべてのネイティブ ECMAScript オブジェクトについて、この仕様によって定義されています。
8.12.8 [[DefaultValue]] (ヒント)から、最終的に起こることはtoString()
、配列で呼び出されて返されることです。ToNumber
この文字列は、上記のように再帰的に送信されます。
ToNumber
では、文字列に対して変換が行われるとどうなるでしょうか? それは9.3.1 ToNumber Applied to the String Typeで説明されており、少し長くなっています。より簡単なのは、変換を直接実行して、何が起こるかを確認することです。
Number(""); // result is 0 on an empty string
Number(" "); // result is 0 on a string with only whitespace
Number("123"); // result is 123 on a numeric string
Number(" 123 ");// result is 123 on a numeric string with leading & trailing spaces
Number("abc"); // result is NaN (not a number) on non-numeric strings
問題は、配列からどの文字列を取得するかです。繰り返しますが、これは簡単にテストできます。
[].toString(); // result is "" (empty string)
結果は空の文字列であり、空の文字列のToNumber
変換は0
上記のとおりであるため、比較していることになります0 === 0
。
次のようにした場合と同じです。
Number( [].toString() ) === 0; // true
または、もう少し引き出すには:
var x = [];
x = x.toString(); // ""
x = Number( x ); // 0
x === 0; // true
より多くのtoString
結果。
toString
配列の変換をさらに表示するには、次のことを考慮してください。
[1].toString(); // "1"
[1,2,3].toString(); // "1,2,3"
["a",1,"b",2].toString(); // "a,1,b,2"
したがってToNumber
、上記の配列で変換を行うと、最初の配列は数値になり、最後の 2 つは になりNaN
ます。
Number([1]); // 1
Number([1,2,3]); // NaN
Number(["a",1,"b",2]); // NaN
いくつかの証拠
このtoString()
変換が変換の前に行われることをさらに証明するために、ToNumber
実際に変更Array.prototype.toString
して別の結果を提供することができ、ToNumber
変換はその変更された結果を使用します。
Array.prototype.toString = function() {
var n = 0;
for( var i = 0; i < this.length; i++ ) {
n += this[i];
}
return n;
};
toString
ここでは、 onArray.prototype
を配列を合計する関数に置き換えました。明らかに、これを行いたくないのですが、どのように異なる結果が得られるかを示すことができます。
Number([1,2,3]); // 6
+[1,2,3]; // 6
ToNumber
これで、以前は結果だった配列の変換が、配列NaN
内の項目の合計になっていることがわかります。
私は自分のベストを尽くします:
[]===0
[]
は0と正確に等しくないため、もちろんfalseです。ただし、[]==0
暗黙のキャストが存在するため、trueです。
+
-[]
プラスまたはマイナスが[]
実数にキャストされるため、機能します。
~0
(0のビット単位の逆数)は-1です。したがって、~[]===-1
動作します。
他のものは、-1を何回も減算または加算するだけで機能します。
私が間違っている場合は修正してください。ただし、配列に追加する (配列に値を追加するのではなく、配列に値を追加するなど) と、数値にキャストされます。残りは、基本的な +, - 演算子 (チルダ (~) はビットごとの NOT です) を使用して数値を変更し、次に方程式を変更するだけです。
So [] == array ([]);
[] + 1 == number (0);
+[]===0 (true)