Nota actualizada: esto se ha solucionado en Chrome 49 .
Muy interesante pregunta! Vamos a profundizar en.
La causa principal
La raíz de la diferencia está en cómo Node.js evalúa estas declaraciones frente a cómo lo hacen las herramientas de desarrollo de Chrome.
Lo que hace Node.js
Node.js usa el módulo repl para esto.
Del código fuente 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);
}
}
);
Esto actúa como si se ejecutara ({}+{})
en las herramientas de desarrollador de Chrome, que también produce "[object Object][object Object]"
lo que cabría esperar.
Lo que hacen las herramientas para desarrolladores de Chrome
Por otro lado, las herramientas de Chrome Dveloper hacen lo siguiente :
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;
}
Entonces, básicamente, realiza un call
en el objeto con la expresión. La expresión es:
with ((window && window.console && window.console._commandLineAPI) || {}) {
{}+{};// <-- This is your code
}
Entonces, como puede ver, la expresión se está evaluando directamente, sin el paréntesis de ajuste.
Por qué Node.js actúa de manera diferente
La fuente de Node.js justifica esto:
// This catches '{a : 1}' properly.
Nodo no siempre actuaba así. Aquí está la confirmación real que lo cambió . Ryan dejó el siguiente comentario sobre el cambio: "Mejore cómo se evaden los comandos REPL" con un ejemplo de la diferencia.
Rinoceronte
Actualización: OP estaba interesado en cómo se comporta Rhino (y por qué se comporta como las herramientas de Chrome y a diferencia de nodejs).
Rhino utiliza un motor JS completamente diferente a diferencia de las herramientas de desarrollador de Chrome y REPL de Node.js, que usan V8.
Aquí está la línea básica de lo que sucede cuando evalúa un comando de JavaScript con Rhino en el shell de Rhino.
Básicamente:
Script script = cx.compileString(scriptText, "<command>", 1, null);
if (script != null) {
script.exec(cx, getShellScope()); // <- just an eval
}
De los tres, el caparazón de Rhino es el que hace lo más parecido a un real eval
sin ningún tipo de envoltura. Rhino's es el más cercano a una eval()
declaración real y puede esperar que se comporte exactamente como lo eval
haría.