Perché {} + {} NaN è solo sul lato client? Perché non in Node.js?


136

While [] + []è una stringa vuota, [] + {}is "[object Object]"e {} + []is 0. Perché è {} + {}NaN?

> {} + {}
  NaN

La mia domanda non è per questo che ({} + {}).toString()è "[object Object][object Object]", mentre NaN.toString()è "NaN", questa parte ha una risposta già qui .

La mia domanda è: perché succede solo sul lato client? Sul lato server ( Node.js ) {} + {}è "[object Object][object Object]".

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

Riassumendo :

Sul lato client:

 [] + []              // 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
È solo la console del browser che lo fa. Prova ad accedere alla console ed è lo stesso di NodeJS. jsbin.com/oveyuj/1/edit
elclanrs

2
Non proprio un duplicato, sto chiedendo la risposta di NodeJS. Votazione per la riapertura ...
Ionică Bizău,

4
Hmm ... scusa. Tuttavia, stackoverflow.com/questions/9032856/… è ancora rilevante e risponde alla prima metà
John Dvorak,

3
Non dimenticare che {}può essere interpretato come un'espressione o come un oggetto primitivo a seconda del contesto. Forse il codice è lo stesso sul client e sul server ma sta interpretando in modo {}diverso a causa del diverso contesto di immissione del codice.
Patashu,

18
Si prega di riaprire e quindi smettere di chiudere questa domanda ancora e ancora poiché questa domanda non è davvero un duplicato .
Alvin Wong,

Risposte:


132

Nota aggiornata: è stato risolto in Chrome 49 .

Domanda molto interessante! Scaviamo dentro.

La causa principale

La radice della differenza sta nel modo in cui Node.js valuta queste affermazioni rispetto a come fanno gli strumenti di sviluppo di Chrome.

Cosa fa Node.js

Node.js utilizza il modulo di sostituzione per questo.

Dal codice sorgente REPL di 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);
        }
    }
);

Questo funziona esattamente come l'esecuzione ({}+{})negli strumenti di sviluppo di Chrome, che produce anche "[object Object][object Object]"come ti aspetteresti.

Cosa fanno gli strumenti per sviluppatori di Chrome

D'altra parte, gli strumenti di Chrome Dveloper eseguono le seguenti operazioni :

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

Quindi, fondamentalmente, esegue un calloggetto sull'espressione. L'espressione è:

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

Quindi, come puoi vedere, l'espressione viene valutata direttamente, senza la parentesi avvolgente.

Perché Node.js si comporta diversamente

La fonte di Node.js giustifica questo:

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

Il nodo non ha sempre agito in questo modo. Ecco il commit effettivo che l'ha modificato . Ryan ha lasciato il seguente commento sulla modifica: "Migliora la valutazione dei comandi REPL" con un esempio della differenza.


Rinoceronte

Aggiornamento: OP era interessato a come Rhino si comporta (e perché si comporta come i devtools di Chrome e diversamente dai nodejs).

Rhino utilizza un motore JS completamente diverso a differenza degli strumenti di sviluppo di Chrome e del REPL di Node.js che utilizzano entrambi V8.

Ecco la linea di base di base di ciò che accade quando si valuta un comando JavaScript con Rhino nella shell di Rhino.

  • La shell funziona org.mozilla.javascript.tools.shell.main.

  • A sua volta, richiede questo new IProxy(IProxy.EVAL_INLINE_SCRIPT); per esempio, se il codice è stato passato direttamente con l'interruttore -e linea.

  • Questo colpisce il runmetodo IProxy .

  • Invoca evalInlineScript( src ). Questo semplicemente compila la stringa e la elimina.

Fondamentalmente:

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

Dei tre, la conchiglia di Rhino è quella che fa la cosa più vicina a un reale evalsenza alcun avvolgimento. Rhino's è il più vicino a eval()un'affermazione reale e puoi aspettarti che si comporti esattamente come evalfarebbe.


1
(Non fa davvero parte della risposta, ma vale la pena ricordare che nodejs usa il modulo vm per valutare di default quando si utilizza REPL e non solo un JavaScript eval)
Benjamin Gruenbaum,

Puoi spiegare perché Rhino , ad esempio, fa la stessa cosa in Terminal (non solo Chrome Console)?
Ionică Bizău,

5
+10 se fosse possibile! Caspita, non hai davvero vita o sei più intelligente di me nel conoscere qualcosa del genere. Per favore, dimmi che hai cercato un po 'per trovare questa risposta :)
Samuel,

7
@ Samuel Basta leggere la fonte - lo giuro! In Chrome, se inserisci "debugger;" , ottieni l'intera pipe: ti porterà direttamente al 'with' con una sola funzione sopra a evaluateOn. Nel nodo, tutto è molto ben documentato - hanno un modulo REPL dedicato con tutta la storia bella e accogliente su git, dopo aver usato i REPL sui miei programmi, sapevo dove cercare :) Sono contento che ti sia piaciuto e trovato è utile, ma lo devo alla mia familiarità con queste basi di codice (dev-tools e nodejs) piuttosto che con il mio intelletto. Andare direttamente alla fonte è spesso sempre il più semplice.
Benjamin Gruenbaum,

Aggiornamento: l'API della console in Chrome è stata aggiornata un po ', quindi mentre l'idea generale qui è corretta, il codice pubblicato non è accurato per la versione più recente di Chrome. Vedi chromium.googlesource.com/chromium/blink.git/+/master/Source/…
Benjamin Gruenbaum,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.