136 votes

Pourquoi {} + {} est NaN seulement sur le côté client? Pourquoi ne pas en Node.js?

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

> {} + {}
  NaN

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

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

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

En résumant:

Sur le 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)

132voto

Benjamin Gruenbaum Points 51406

Question très intéressante! Nous allons creuser dans.

La cause racine

La racine de la différence est dans la façon dont Node.js évalue ces déclarations contre la façon dont le Chrome des outils de développement ne.

Ce Node.js n'

Node.js utilise le repl module pour cela.

À partir de la Node.js REPL code source:

  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 agit comme exécutant ({}+{}) dans les outils de développement Chrome, qui produit également de l' "[object Object][object Object]" comme vous le souhaitez.

Ce que les outils de développement chrome ne

Sur l'autre main Chrome dveloper les outils suivants:

  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:

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

Donc, comme vous pouvez le voir, l'expression est effectuée directement, sans l'habillage de la parenthèse.

Pourquoi Node.js actes différemment

Node.js source justifie ce:

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

Le nœud n'a pas toujours agir de la sorte. Ici est le réel s'engager à ce que l'a changé. Ryan laissé le commentaire suivant sur le changement: "Améliorer la façon dont REPL commandes sont evaled" avec un exemple de la différence.


Rhino

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

Rhino utilise un complètement différent JS moteur contrairement aux outils de développement Chrome et Node.js's REPL qui utilisent l'V8.

Ici est la base de la ligne de la pipe de ce qui se passe quand vous eval une commande JavaScript avec Rhino dans le Rhino shell.

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

  • À son tour, il appelle cette new IProxy(IProxy.EVAL_INLINE_SCRIPT); par exemple, si le code a été transmis directement à la ligne de l'interrupteur -e.

  • Cette frappe IProxy de l' run méthode.

  • Il invoque evalInlineScript (src). Il suffit de compile de la chaîne et est évaluée comme elle.

En gros:

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

Des trois, Rhino shell est celui qui fait la chose la plus proche d'un réel eval sans emballage. Rhino est le plus proche d'un réel eval() déclaration et vous pouvez vous attendre qu'il se comporte exactement comme eval serait.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X