web-dev-qa-db-ja.com

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

_[] + []_は空の文字列ですが、_[] + {}_は_"[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)
_
136
Ionică Bizău

注を更新: これは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コマンドが評価される方法を改善する」)違いの例。


サイ

更新-OPは、Rhinoの動作に興味がありました(そして、なぜChrome devtoolsとnodejsとは異なる動作をするのか)。

Rhinoは、両方ともV8を使用するChrome開発者ツールとNode.jsのREPLとは異なり、まったく異なるJSエンジンを使用します。

RhinoシェルでRhinoを使用してJavaScriptコマンドを評価した場合の基本的なパイプラインを次に示します。

  • シェルは _org.mozilla.javascript.tools.Shell.main_ を実行します。

  • 次に、コードがインラインスイッチ-eで直接渡された場合、たとえば thisnew 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は実際のeval()ステートメントに最も近く、evalとまったく同じように動作することが期待できます。

131