この記事は、Gary Bernhardtの素晴らしい”Wat”トークへのオマージュで、RubyとJavaScriptのいくつかの言語構 あなたはまだ話を見ていない場合は、私は強くあなたが時間をかけて、正確にそれを行うことをお勧めします! それは約4分の長さと非常に面白いです、私は約束します。
彼の講演では、ゲイリーはJavaScriptコードのこれらの四つの断片を披露しています:
括弧、中括弧、プラス記号がたくさんあります。 これらの断片が評価するものは次のとおりです:
+ == ""
+ {} == ""
{} + == 0
{} + {} == NaN
私が初めてこれらの例を見たとき、私は思った:”うわー、それは乱雑に見えます!”結果は矛盾しているか、恣意的に見えるかもしれませんが、ここで私と一緒に負担してください。 これらの例はすべて、実際には非常に一貫しており、見た目ほど悪くはありません!
#1: +
最初の断片から始めましょう:
+ // ""
ご覧のとおり、+
演算子を2つの空の配列に適用すると、空の文字列になります。 これは、配列の文字列表現が、そのすべての要素の文字列表現であり、カンマと連結されているためです:
.toString()// "1,2,3".toString()// "1,2".toString()// "1".toString()// ""
空の配列には要素が含まれていないため、その文字列表現は空の文字列です。 したがって、2つの空の文字列の連結は、単に別の空の文字列です。
#2: + {}
これまでのところ、とても良い。 それでは、第二の断片を調べてみましょう:
+ {}// ""
2つの数値を扱っていないため、+
演算子は2つの数値を加算するのではなく、文字列の連結を再度実行することに注意してください。
前のセクションでは、空の配列の文字列表現が空の文字列であることをすでに見てきました。 ここでの空のオブジェクトリテラルの文字列表現は、デフォルトの""
値です。 空の文字列の前に追加しても値は変更されないため、""
が最終結果です。
JavaScriptでは、オブジェクトはメソッドが呼び出されたオブジェクトのカスタム文字列表現を返すtoString()
と呼ばれる特別なメソッドを実装できます。 空のオブジェクトリテラルはそのようなメソッドを実装していないので、Object
プロトタイプのデフォルトの実装にフォールバックしています。
#3: {} +
私はこれまでのところ、結果はあまりにも予想外ではなかったと主張するだろう。 彼らは単にJavaScriptの型強制とデフォルトの文字列表現の規則に従ってきました。
しかし、{} +
は開発者が混乱し始める場所です:
{} + // 0
上記の行をブラウザコンソールのようなJavaScript REPLに入力すると、なぜ0
(数値ゼロ)が表示されるのですか? 結果は + {}
のように文字列になるべきではありませんか?
謎を解く前に、+
演算子を使用できる三つの異なる方法を考えてみましょう:
// 1) Addition of two numeric values2 + 2 == 4// 2) String concatenation of two values"2" + "2" == "22"// 3) Conversion of a value to a number+2 == 2+"2" == 2
最初の2つのケースでは、+
演算子は2つのオペランド(左と右)を持つため、2項演算子です。 3番目のケースでは、+
演算子は単一のオペランド(右側)しか持たないため、単項演算子です。
また、JavaScriptで{}
の二つの可能な意味を考えてみましょう。 通常、{}
は空のオブジェクトリテラルを意味しますが、ステートメントの位置にある場合、JavaScript文法は{}
を空のブロックを意味するように指定します。 次のコードでは、2つの空のブロックを定義していますが、どれもオブジェクトリテラルではありません:
{}// Empty block{ // Empty block}
のは、再び私たちの断片を見てみましょう:
{} +
JavaScriptエンジンがコードをどのように認識するかを明確にするために、空白を少し変更してみましょう:
{ // Empty block}+;
ここで何が起こっているのかをはっきりと見ることができます。 ブロック文の後に、空の配列で動作する単項+
式を含む別の文があります。 末尾のセミコロンは、ASI(自動セミコロン挿入)の規則に従って自動的に挿入されます。
ブラウザコンソールで+
が0
に評価されることを簡単に確認できます。 空の配列には、文字列表現として空の文字列があり、それは+
演算子によって数値ゼロに変換されます。 最後に、最後の文(この場合は+
)の値がブラウザコンソールによって報告されます。
または、両方のコードスニペットをEsprimaなどのJavaScriptパーサーにフィードし、結果の抽象構文ツリーを比較することもできます。 ここにASTがあります + {}
:
{ "type": "Program", "body": }, "right": { "type": "ObjectExpression", "properties": } } } ], "sourceType": "script"}
そして、ここでのためのASTです{} +
:
{ "type": "Program", "body": }, { "type": "ExpressionStatement", "expression": { "type": "UnaryExpression", "operator": "+", "argument": { "type": "ArrayExpression", "elements": }, "prefix": true } } ], "sourceType": "script"}
この混乱は、オブジェクトリテラルとブロックの両方に中括弧を使用するJavaScript文法のニュアンスに起因しています。 文の位置では、開始中括弧がブロックを開始し、式の位置では開始中括弧がオブジェクトリテラルを開始します。
#4: {} + {}
最後に、すぐに私たちの最後の断片を見てみましょう{} + {}
:
{} + {}// NaN
さて、二つのオブジェクトリテラルを追加することは文字通り”数ではありません”—しかし、我々はここに二つのオブジェク 中括弧が再びあなたを欺くことはできません! これは何が起こっているのかです:
{ // Empty block}+{};
これは、前の例とほぼ同じ処理です。 しかし、空のオブジェクトリテラルに単項プラス演算子を適用しています。 これは基本的にNumber({})
を実行するのと同じですが、オブジェクトリテラルは数値に変換できないため、NaN
になります。JavaScriptエンジンでコードを2つの空のオブジェクトリテラルとして解析する場合は、最初のもの(またはコード全体)をかっこで囲みます。 これで、期待される結果が表示されます:
({}) + {}// ""({} + {})// ""
開き括弧は、パーサーが式を認識しようとする原因となるため、{}
をブロック(ステートメント)として扱わない理由です。
#Summary
四つのコードフラグメントが彼らのやり方を評価する理由がわかります。 型強制の規則は、仕様書と言語文法に記載されているとおりに正確に適用されます。
開始中括弧が文の最初の文字である場合、オブジェクトリテラルではなくブロックの開始文字として解釈されることに注意してください。