1. ホーム
  2. javascript

[解決済み] CodeMash 2012の'Wat'トークで言及された、この奇妙なJavaScriptの動作の説明とは?

2022-03-22 16:25:36

質問

その CodeMash 2012の「Wat」トーク RubyとJavaScriptの奇妙な癖をいくつか指摘しています。

その結果をJSFiddleにしました。 http://jsfiddle.net/fe479/9/ .

JavaScript特有の動作は(Rubyを知らないので)以下の通りです。

JSFiddleでは、私の結果がビデオの結果と一致しないものがありましたが、その理由はよくわかりません。しかし、私はそれぞれのケースでJavaScriptが裏でどのように処理されているのか知りたいと思っています。

Empty Array + Empty Array
[] + []
result:
<Empty String>

がかなり気になるところです。 + 演算子を使用することができます。 これはビデオの結果と一致します。

Empty Array + Object
[] + {}
result:
[Object]

これは動画の結果と一致します。どうなっているんだろう?なぜこれがオブジェクトなのか。は何をするのでしょうか? + 演算子は何をするのですか?

Object + Empty Array
{} + []
result:
[Object]

これはビデオと一致しない。ビデオでは結果が0になるようですが、私は[Object]を得ました。

Object + Object
{} + {}
result:
[Object][Object]

これも動画と一致しないし、変数を出力するとどうして2つのオブジェクトになるのか?私のJSFiddleがおかしいのかもしれません。

Array(16).join("wat" - 1)
result:
NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN

wat + 1 を実行すると wat1wat1wat1wat1 ...

これは、文字列から数値を引こうとするとNaNになってしまうという、素直な動作なのではないでしょうか。

解決方法は?

あなたが見ている(と思われる)結果について、解説をまとめました。使用しているリファレンスは ECMA-262規格 .

  1. [] + []

    加算演算子を使用する場合、左右のオペランドはともに最初にプリミティブに変換されます( §11.6.1 ). と同様に §9.1 オブジェクト(この場合は配列)をプリミティブに変換すると、そのデフォルト値が返されます。 toString() メソッドを呼び出した結果です。 object.toString() ( §8.12.8 ). 配列の場合、これは以下のように呼び出すのと同じです。 array.join() ( §15.4.4.2 ). 空の配列を結合すると空の文字列になるので、加算演算子のステップ#7は2つの空の文字列の連結を返し、それは空の文字列となります。

  2. [] + {}

    と同様です。 [] + [] の場合、両オペランドはまずプリミティブに変換される。オブジェクトオブジェクト(15.2)の場合、これは再び object.toString() NULLでない、未定義でないオブジェクトの場合は、次のようになります。 "[object Object]" ( §15.2.4.2 ).

  3. {} + []

    {} はオブジェクトとしてではなく、空のブロックとしてパースされます ( §12.1 少なくとも、そのステートメントを無理に式にしない限りは、ですが、それについては後で詳しく説明します)。空のブロックの戻り値は空なので、その文の結果は次のものと同じです。 +[] . 単項の + 演算子( §11.4.6 を返します。 ToNumber(ToPrimitive(operand)) . すでにご存知のように ToPrimitive([]) は空の文字列であり §9.3.1 , ToNumber("") は0である。

  4. {} + {}

    先ほどのケースと同様に、最初の {} は空の戻り値を持つブロックとしてパースされます。もう一度 +{} と同じです。 ToNumber(ToPrimitive({})) であり、かつ ToPrimitive({})"[object Object]" (参照 [] + {} ). そのため、結果を得るために +{} を適用する必要があります。 ToNumber という文字列の上に "[object Object]" . の手順を踏むと §9.3.1 となります。 NaN ということになります。

    の展開として解釈できない場合、文法はその文字列を 文字列数値リテラル の結果は 数値へ NaN .

  5. Array(16).join("wat" - 1)

    による §15.4.1.1 および §15.4.2.2 , Array(16) は長さ16の新しい配列を作成します。結合する引数の値を取得する。 §11.6.2 ステップ #5 と #6 では、両方のオペランドを数値に変換するために ToNumber . ToNumber(1) は単純に1( §9.3 ) であるのに対し ToNumber("wat") はまた NaN と同じように §9.3.1 . の手順 7 に従います。 §11.6.2 , §11.6.3 は次のように定めています。

    もし、どちらかのオペランドが NaN の場合、その結果は NaN .

    ということは、引数で Array(16).joinNaN . 15.4.4.5に従って( Array.prototype.join を呼び出す必要があります。 ToString である引数に "NaN" ( §9.8.1 ):

    もし m NaN という文字列を返します。 "NaN" .

    のステップ10に続いて §15.4.4.5 の連結が15回繰り返されることになります。 "NaN" と空の文字列が表示され、あなたが見ている結果と同じになります。 このとき "wat" + 1 の代わりに "wat" - 1 を引数として与えると、加算演算子は 1 を文字列に変換するのではなく "wat" を数値に変換しているので、実質的に Array(16).join("wat1") .

について、異なる結果が表示される理由については、次のとおりです。 {} + [] の場合です。関数の引数として使用する場合、ステートメントを強制的に ExpressionStatement をパースすることができなくなります。 {} を空のブロックとして解析し、その代わりに空のオブジェクト リテラルとして解析します。