Warum ist {} + {} NaN nur auf der Client-Seite? Warum nicht in Node.js?


136

Während [] + []ist eine leere Zeichenfolge, [] + {}ist "[object Object]"und {} + []ist 0. Warum ist {} + {}NaN?

> {} + {}
  NaN

Meine Frage ist nicht , warum ({} + {}).toString()ist "[object Object][object Object]"während NaN.toString()ist "NaN", dieser Teil bereits eine Antwort hier hat .

Meine Frage ist, warum dies nur auf der Client-Seite geschieht. Auf der Serverseite ( Node.js ) {} + {}ist "[object Object][object Object]".

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

Zusammenfassend :

Auf der Client-Seite:

 [] + []              // 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]"

In 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
Das macht nur die Browserkonsole. Versuchen Sie, sich an der Konsole anzumelden, und es ist dasselbe wie in NodeJS. jsbin.com/oveyuj/1/edit
elclanrs

2
Nicht wirklich ein Duplikat, ich bitte um NodeJS Antwort. Abstimmung für die Wiedereröffnung ...
Ionică Bizău

4
Hmm ... Entschuldigung. Allerdings stackoverflow.com/questions/9032856/... ist immer noch relevant und beantwortet die erste Hälfte
John Dvorak

3
Vergessen Sie nicht, dass {}dies je nach Kontext entweder als Ausdruck oder als Objektprimitiv interpretiert werden kann. Möglicherweise ist der Code auf dem Client und auf dem Server gleich, wird jedoch {}aufgrund des unterschiedlichen Kontextes für die Eingabe des Codes unterschiedlich interpretiert .
Patashu

18
Bitte öffnen Sie diese Frage erneut und schließen Sie sie nicht mehr, da diese Frage wirklich kein Duplikat ist .
Alvin Wong

Antworten:


132

Aktualisierter Hinweis: Dies wurde in Chrome 49 behoben .

Sehr interessante Frage! Lass uns eintauchen.

Die Grundursache

Die Wurzel des Unterschieds liegt darin, wie Node.js diese Aussagen im Vergleich zu den Chrome-Entwicklungstools bewertet.

Was Node.js macht

Node.js verwendet hierfür das Repl- Modul.

Aus dem REPL-Quellcode von Node.js :

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);
        }
    }
);

Dies funktioniert genauso wie das Ausführen ({}+{})in den Chrome-Entwicklertools, die ebenfalls "[object Object][object Object]"wie erwartet produzieren.

Was die Chrome-Entwicklertools tun

Auf der anderen Seite führt Chrome Dveloper Tools Folgendes aus :

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;
}

Im Grunde führt es ein callObjekt mit dem Ausdruck aus. Der Ausdruck ist:

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

Wie Sie sehen können, wird der Ausdruck direkt ohne die umschließende Klammer ausgewertet.

Warum Node.js anders verhält

Die Quelle von Node.js rechtfertigt dies:

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

Node hat sich nicht immer so verhalten. Hier ist das tatsächliche Commit, das es geändert hat . Ryan hinterließ den folgenden Kommentar zu der Änderung: "Verbessern Sie die Auswertung von REPL-Befehlen" mit einem Beispiel für den Unterschied.


Nashorn

Update - OP war daran interessiert, wie sich Rhino verhält (und warum es sich wie die Chrome-Devtools und im Gegensatz zu NodeJS verhält).

Rhino verwendet eine völlig andere JS-Engine als die Chrome-Entwicklertools und die REPL von Node.js, die beide V8 verwenden.

Hier ist die grundlegende Pipeline, was passiert, wenn Sie einen JavaScript-Befehl mit Rhino in der Rhino-Shell auswerten.

  • Die Shell läuft org.mozilla.javascript.tools.shell.main.

  • Im Gegenzug fordert er dies new IProxy(IProxy.EVAL_INLINE_SCRIPT); beispielsweise, wenn der Code direkt mit dem Inline - Schalter -en übergeben wurde.

  • Dies trifft die IProxy- runMethode.

  • Es ruft evalInlineScript( src ) auf. Dadurch wird der String einfach kompiliert und ausgewertet.

Grundsätzlich:

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

Von den dreien ist Rhinos Muschel diejenige, die einer tatsächlichen Sache am nächsten kommt, evalohne sie einzuwickeln. Rhino's kommt einer tatsächlichen eval()Aussage am nächsten und Sie können erwarten, dass sie sich genau so verhält, wie sie es tun evalwürde.


1
(Nicht wirklich ein Teil der Antwort, aber erwähnenswert, dass nodejs das vm-Modul standardmäßig für die Auswertung verwendet, wenn die REPL verwendet wird, und nicht nur ein JavaScript eval)
Benjamin Gruenbaum

Können Sie erklären, warum Nashorn beispielsweise im Terminal (nicht nur in der Chrome-Konsole) dasselbe tut?
Ionică Bizău

5
+10 wenn es möglich war! Wow Mann, ... Du hast wirklich kein Leben oder du bist wirklich schlauer als ich, um so etwas zu wissen. Bitte sagen Sie mir, dass Sie ein wenig gesucht haben, um diese Antwort zu finden :)
Samuel

7
@ Samuel Alles was es brauchte ist die Quelle zu lesen - ich schwöre! Wenn Sie in Chrome "Debugger" eingeben, , du bekommst die ganze Pipe - sie wirft dich direkt zum 'mit' mit nur einer Funktion über evaluateOn. In Node ist alles sehr gut dokumentiert - sie haben ein dediziertes REPL-Modul mit der ganzen Geschichte, nett und gemütlich auf Git. Nachdem ich REPLs zuvor in meinen eigenen Programmen verwendet hatte, wusste ich, wo ich suchen musste :) Ich bin froh, dass es Ihnen gefallen hat und Sie es gefunden haben es ist hilfreich, aber ich verdanke es eher meiner Vertrautheit mit diesen Codebasen (dev-tools und nodejs) als meinem Intellekt. Es ist oft immer am einfachsten, direkt zur Quelle zu gehen.
Benjamin Gruenbaum

Update - Die Konsolen-API in Chrome wurde ein wenig aktualisiert. Obwohl die allgemeine Idee hier korrekt ist, ist der veröffentlichte Code für die neueste Version von Chrome nicht korrekt. Siehe chrom.googlesource.com/chromium/blink.git/+/master/Source/…
Benjamin Gruenbaum
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.