web-dev-qa-db-fra.com

Pourquoi {} + {} NaN est-il uniquement côté client? Pourquoi pas dans Node.js?

Alors que [] + [] Est une chaîne vide, [] + {} Est "[object Object]" Et {} + [] Est 0. Pourquoi {} + {} NaN?

> {} + {}
  NaN

Ma question n'est pas pourquoi ({} + {}).toString() Est "[object Object][object Object]" Tandis que NaN.toString() est "NaN", cette partie a déjà une réponse ici .

Ma question est pourquoi cela se produit-il uniquement du côté client? Côté serveur ( Node.js ) {} + {} Est "[object Object][object Object]".

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

Résumé :

Côté 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]"

Dans 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

Note mise à jour: cela a été corrigé dans Chrome 49 .

Question très intéressante! Creusons.

La cause-racine

La racine de la différence réside dans la façon dont Node.js évalue ces instructions par rapport à la façon dont les outils de développement Chrome font.

Que fait Node.js

Node.js utilise pour cela le module repl .

Depuis Node.js code source 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);
        }
    }
);

Cela revient à exécuter ({}+{}) Dans les outils de développement Chrome, qui produisent également "[object Object][object Object]" Comme vous vous en doutez.

Ce que font les outils de développement chrome

D'un autre côté les outils Chrome dveloper font ce qui suit :

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

Donc, fondamentalement, il effectue un call sur l'objet avec l'expression. L'expression étant:

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

Ainsi, comme vous pouvez le voir, l'expression est évaluée directement, sans la parenthèse d'habillage.

Pourquoi Node.js agit différemment

La source de Node.js le justifie:

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

Node n'a pas toujours agi ainsi. Voici le commit réel qui l'a changé . Ryan a laissé le commentaire suivant sur le changement: "Améliorez la façon dont les commandes REPL sont évaluées" avec un exemple de la différence.


Rhinocéros

Mise à jour - OP était intéressé par la façon dont Rhino se comportait (et pourquoi il se comportait comme Chrome devtools et contrairement à nodejs).

Rhino utilise un moteur JS complètement différent contrairement aux outils de développement Chrome et Node.js REPL qui utilisent tous les deux la V8).

Voici le pipeline de base de ce qui se passe lorsque vous évaluez une commande JavaScript avec Rhino dans Rhino Shell.

  • Le shell s'exécute org.mozilla.javascript.tools.Shell.main .

  • À son tour, il appelle thisnew IProxy(IProxy.EVAL_INLINE_SCRIPT); par exemple, si le code a été passé directement avec le commutateur en ligne -e.

  • Cela atteint la méthode run d'IProxy.

  • Il appelle evalInlineScript ( src ). Cela compile simplement la chaîne et l'évalue.

Fondamentalement:

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

Sur les trois, Rhino's Shell est celui qui fait le plus proche d'un eval réel sans aucun emballage. Rhino est le plus proche d'une instruction eval() réelle et vous pouvez vous attendre à ce qu'elle se comporte exactement comme eval.

131
Benjamin Gruenbaum