1. ホーム
  2. javascript

[解決済み] なぜ{}なのですか?+ {}がNaNになるのはクライアント側だけですか?なぜNode.jsではダメなのか?

2022-06-12 23:13:53

質問

一方 [] + [] は空文字列です。 [] + {}"[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)

どのように解決するのですか?

注意事項を更新しました。 は、Chrome 49 で修正されました。 .

とても興味深い質問ですね。掘り下げてみましょう。

根本的な原因

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

Node.jsが行うこと

Node.jsは を複製します。 モジュールを使います。

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]" が表示されます。

クロームデベロッパーツールでできること

一方 クロームデベロッパーツールでは以下のことができます。 :

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.

Nodeは常にこのように動作するわけではありませんでした。ここでは を変更した実際のコミットです。 . Ryan はこの変更に対して次のコメントを残しています: "REPLコマンドが評価される方法を改善する" その違いの例もあります。


Rhino

更新 - OP が興味を持ったのは Rhino がどのように動作するのか(そして、なぜChromeの開発ツールと同じように動作し、nodejsと違うのか)に興味を持ちました。

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

RhinoのシェルでJavaScriptのコマンドを評価するときの基本的なパイプラインは以下のとおりです。

  • シェルが実行する org.mozilla.javascript.tools.shell.main .

  • 順番に、それは この new IProxy(IProxy.EVAL_INLINE_SCRIPT); 例えば、インラインスイッチ -e で直接渡された場合。

  • これは 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 のような動作を期待できます。