137

While[] + []は空文字列、[] + {}is "[object Object]"、および{} + []is0です。なぜ{} + {}NaNなのですか?

> {} + {}
  NaN

私の質問はなぜ({} + {}).toString()is "[object Object][object Object]"while NaN.toString()isではなく"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

1 に答える 1

133

更新されたメモ:これは 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 開発者ツールは次のことを行います

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、式を使用してオブジェクトに対して a を実行します。式は次のとおりです。

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

ご覧のとおり、式は括弧なしで直接評価されています。

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

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

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

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


ライノ

更新 - OP は、 Rhinoがどのように動作するか (そしてなぜそれが Chrome devtools のように動作し、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メソッドに当たります。

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

基本的:

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

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

于 2013-06-24T06:39:05.333 に答える