クライアント側でのみ{} + {}がNaNになるのはなぜですか?Node.jsにないのはなぜですか?


136

一方では[] + []、空の文字列で[] + {}あり"[object Object]"、かつ{} + []あります0。なぜ{} + {}NaNなのですか?

> {} + {}
  NaN

なぜ私の質問ではありません({} + {}).toString()されて"[object Object][object Object]"いる間NaN.toString()"NaN"この部分はすでにここに答えを持っています

私の質問は、これがクライアント側でのみ発生するのはなぜですか?サーバー側(Node.js{} + {}"[object Object][object Object]"です。

> {} + {}
'[object Object][object Object]'

まとめ

クライアント側:

 [] + []              // Returns ""
 [] + {}              // Returns "[object Object]"
 {} + []              // Returns 0
 {} + {}              // Returns NaN

 NaN.toString()       // Returns "NaN"
 ({} + {}).toString() // Returns "[object Object][object Object]"
 var a = {} + {};     // 'a' will be "[object Object][object Object]"

Node.js:

 [] + []   // Returns "" (like on the client)
 [] + {}   // Returns "[object Object]" (like on the client)
 {} + []   // Returns "[object Object]" (not like on the client)
 {} + {}   // Returns "[object Object][object Object]" (not like on the client)

4
それを行うのはブラウザコンソールだけです。コンソールにログインてみてください。これはNodeJSと同じです。jsbin.com/oveyuj/1/edit
elclanrs

2
実際には重複していません。NodeJSの回答を求めています。再度開く...のための投票
IonicăBizău

4
うーん...ごめんなさい。ただし、stackoverflow.com / questions / 9032856 /…は依然として関連性があり、前半に答えます
John Dvorak 2013年

3
{}コンテキストに応じて、式またはオブジェクトプリミティブとして解釈できることを忘れないでください。たぶん、コードはクライアントとサーバーで同じですが、コード{}を入力するコンテキストが異なるため、解釈が異なります。
Patashu 2013年

18
この質問は実際には重複していないため、この質問をもう一度開いて閉じないでください
Alvin Wong

回答:


132

更新されたメモ:これはChrome 49で修正されました

非常に興味深い質問です。掘り下げましょう。

根本的な原因

違いの根本は、Node.jsがこれらのステートメントを評価する方法とChrome開発ツールが行う方法にあります。

Node.jsの機能

Node.jsはこのためにreplモジュールを使用します

Node.js REPLソースコードから

self.eval(
    '(' + evalCmd + ')',
    self.context,
    'repl',
    function (e, ret) {
        if (e && !isSyntaxError(e))
            return finish(e);
        if (typeof ret === 'function' && /^[\r\n\s]*function/.test(evalCmd) || e) {
            // Now as statement without parens.
            self.eval(evalCmd, self.context, 'repl', finish);
        }
        else {
            finish(null, ret);
        }
    }
);

これ({}+{})は、Chromeデベロッパーツールで実行するのと同じように動作し、"[object Object][object Object]"期待どおりに生成されます。

Chromeデベロッパーツールの機能

一方、Chrome dveloperツールは次のことを行います

try {
    if (injectCommandLineAPI && inspectedWindow.console) {
        inspectedWindow.console._commandLineAPI = new CommandLineAPI(this._commandLineAPIImpl, isEvalOnCallFrame ? object : null);
        expression = "with ((window && window.console && window.console._commandLineAPI) || {}) {\n" + expression + "\n}";
    }
    var result = evalFunction.call(object, expression);
    if (objectGroup === "console")
        this._lastResult = result;
    return result;
}
finally {
    if (injectCommandLineAPI && inspectedWindow.console)
        delete inspectedWindow.console._commandLineAPI;
}

したがって、基本的にcallは、式を使用してオブジェクトに対してを実行します。式は次のとおりです。

with ((window && window.console && window.console._commandLineAPI) || {}) {
    {}+{};// <-- This is your code
}

ご覧のように、式は、括弧を付けずに直接評価されています。

Node.jsの動作が異なる理由

Node.jsのソースはこれを正当化します:

// This catches '{a : 1}' properly.

ノードは常にこのように動作するとは限りませんでした。ここでされているが、それを変更し、実際のコミット。ライアンは変更について次のコメントを残しました:「REPLコマンドの評価方法の改善」とその違いの例。


Rhino

更新-OPは、Rhinoの動作方法(およびなぜそれがChrome開発ツールのように動作し、nodejsとは異なる動作をするか)に関心がありました。

Rhinoは、V8を使用するChrome開発者ツールやNode.jsのREPLとは異なり、完全に異なるJSエンジンを使用します。

RhinoシェルでRhinoを使用してJavaScriptコマンドを評価すると、何が起きるかの基本的なパイプラインは次のとおりです。

  • シェルが実行されorg.mozilla.javascript.tools.shell.mainます。

  • 次に、コードがインラインスイッチ-eで直接渡された場合などに、これ new IProxy(IProxy.EVAL_INLINE_SCRIPT);を呼び出します。

  • これはIProxyのrun方法に当てはまります。

  • evalInlineScriptsrc)を呼び出します。これは単に文字列をコンパイルして評価します。

基本的に:

Script script = cx.compileString(scriptText, "<command>", 1, null);
if (script != null) {
    script.exec(cx, getShellScope()); // <- just an eval
}

3つのうち、Rhinoのシェルは、evalラッピングなしで実際のシェルに最も近いシェルです。Rhino'sは実際のeval()ステートメントに最も近く、実際の動作と同じように動作することが期待できますeval


1
(実際には答えの一部ではありませんが、nodejsは、JavaScriptだけでなく、REPLを使用する場合、デフォルトでvmモジュールを使用して評価するeval
Benjamin Gruenbaum

例えば、rhinoが(Chromeコンソールだけでなく)ターミナルでも同じことをする理由を説明できますか?
IonicăBizău

5
+10可能なら!うわー、あなたは本当に人生を持っていないか、あなたはそのようなことを知っている私より本当に賢いです。この答えを見つけるために少し検索したことを教えてください:)
サミュエル

7
@Samuel必要なのはソースを読むことだけです-私は誓います!Chromeで「デバッガ」と入力した場合 、あなたはパイプ全体を取得します-それは上の1つの関数だけであなたを「with」に直接投げますevaluateOn。ノードでは、すべてが非常によく文書化されています-自分のプログラムで以前にREPLを使用したことがある、gitですべての履歴が素敵で居心地の良い専用のREPLモジュールがあります。それは役に立ちましたが、私が知性ではなく、これらのコードベース(dev-toolsとnodejs)に精通していることが原因です。多くの場合、ソースに直接アクセスするのが最も簡単です。
Benjamin Gruenbaum 2013年

更新-ChromeのコンソールAPIが少し更新されたため、ここでの一般的な考え方は正しいですが、投稿されたコードはChromeの最新バージョンでは正確ではありません。chromium.googlesource.com/chromium/blink.git/+/master/Source/…を
Benjamin Gruenbaum 2014年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.