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

136

While [] + []est une chaîne vide, [] + {}est "[object Object]"et {} + []est 0. Pourquoi {} + {}NaN?

> {} + {}
  NaN

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

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

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

En 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)
Ionică Bizău
la source
4
C'est juste la console du navigateur qui fait cela. Essayez de vous connecter à la console et c'est la même chose que dans NodeJS. jsbin.com/oveyuj/1/edit
elclanrs
2
Pas vraiment un doublon, je demande une réponse NodeJS. Voting for
reopen
4
Hmm ... désolé. Cependant, stackoverflow.com/questions/9032856/… est toujours d'actualité et répond à la première moitié
John Dvorak
3
N'oubliez pas que cela {}peut être interprété comme une expression ou comme une primitive d'objet selon le contexte. Peut-être que le code est le même sur le client et sur le serveur, mais il est interprété {}différemment en raison du contexte différent de la saisie du code.
Patashu
18
Veuillez rouvrir et arrêter de fermer cette question encore et encore car cette question n'est vraiment pas un doublon .
Alvin Wong

Réponses:

132

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

Question très intéressante! Allons creuser.

La cause-racine

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

Ce que fait Node.js

Node.js utilise le module repl pour cela.

À partir du code source 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);
        }
    }
);

Cela fonctionne comme si vous ({}+{})exécutiez les outils de développement Chrome, qui produisent également "[object Object][object Object]"comme vous vous y attendez.

Que font les outils de développement Chrome

D'autre part, les outils de développement Chrome effectuent les opérations suivantes :

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 callsur 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 justifie ceci:

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

Node n'a pas toujours agi comme ça. 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 le comportement de Rhino (et pourquoi il se comporte comme les outils de développement Chrome et contrairement à nodejs).

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

Voici la ligne de conduite de base de ce qui se passe lorsque vous évaluez une commande JavaScript avec Rhino dans le shell Rhino.

  • Le shell fonctionne org.mozilla.javascript.tools.shell.main.

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

  • Cela frappe IProxy run méthode .

  • Il invoque 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, la coquille de Rhino est celle qui fait la chose la plus proche d'un réel evalsans aucun emballage. Rhino est le plus proche d'une eval()déclaration réelle et vous pouvez vous attendre à ce qu'il se comporte exactement comme le evalferait.

Benjamin Gruenbaum
la source
1
(Cela ne fait pas vraiment partie de la réponse, mais il convient de mentionner que nodejs utilise le module vm pour évaluer par défaut lors de l'utilisation du REPL, et pas seulement un JavaScript eval)
Benjamin Gruenbaum
Pouvez-vous expliquer pourquoi rhino , par exemple, fait la même chose dans Terminal (pas seulement dans Chrome Console)?
Ionică Bizău
5
+10 si c'était possible! Wow mec, ... Tu n'as vraiment pas de vie ou tu es vraiment plus intelligent que moi pour savoir quelque chose comme ça. S'il vous plaît, dites-moi que vous avez cherché un peu pour trouver cette réponse :)
Samuel
7
@Samuel Il suffit de lire la source - je le jure! Dans Chrome, si vous entrez "debugger;" , vous obtenez le tube entier - il vous jettera directement vers le «avec» avec une seule fonction ci-dessus evaluateOn. Dans node, tout est très bien documenté - ils ont un module REPL dédié avec toute l'histoire agréable et confortable sur git, ayant utilisé des REPL auparavant sur mes propres programmes, je savais où chercher :) Je suis heureux que vous ayez aimé et trouvé c'est utile, mais je le dois à ma familiarité avec ces bases de code (dev-tools et nodejs) plutôt qu'à mon intellect. Aller directement à la source est souvent toujours le plus simple.
Benjamin Gruenbaum
Mise à jour - l'API de la console dans Chrome a été légèrement mise à jour, donc bien que l'idée générale ici soit correcte, le code publié n'est pas précis pour la version la plus récente de Chrome. Voir chromium.googlesource.com/chromium/blink.git/+/master/Source/…
Benjamin Gruenbaum