Ответ 1
Обновлено примечание: это было исправлено в Chrome 49.
Очень интересный вопрос! Позвольте вставить.
Основная причина
Корень различия заключается в том, как Node.js оценивает эти операторы, как это делают инструменты разработки Chrome.
Что Node.js делает
Node.js использует repl для этого.
Из 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);
}
}
);
Это действует так же, как запуск ({}+{})
в инструментах разработчика Chrome, который также создает "[object Object][object Object]"
, как и следовало ожидать.
Что делают инструменты для создания хрома
С другой стороны Инструменты разработчика Chrome делают следующее:
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;
}
В основном, он выполняет call
на объекте с выражением. Выражение:
with ((window && window.console && window.console._commandLineAPI) || {}) {
{}+{};// <-- This is your code
}
Итак, как вы можете видеть, выражение вычисляется напрямую, без скобок.
Почему Node.js действует по-разному
Node.js источник оправдывает это:
// This catches '{a : 1}' properly.
Node не всегда так поступал. Вот фактическая фиксация, которая изменила ее. Райан оставил следующий комментарий об изменении: "Улучшите, как команды REPL обнуляются", с примером разницы.
Rhino
Update - OP интересовался тем, как ведет себя Rhino (и почему он ведет себя как Chrome devtools и в отличие от nodejs).
Rhino использует совершенно другой JS-движок в отличие от инструментов разработчика Chrome и Node.js REPL, которые используют V8.
Вот базовая строка, что происходит, когда вы вычисляете команду JavaScript с Rhino в оболочке Rhino.
-
Оболочка запускает
org.mozilla.javascript.tools.shell.main
. -
В свою очередь, он вызывает this
new IProxy(IProxy.EVAL_INLINE_SCRIPT);
, например, если код передавался непосредственно с встроенным коммутатором -e. -
Это вызывает метод IProxy
run
. -
Он вызывает
evalInlineScript
(src). Это просто компилирует строку и оценивает ее.
В принципе:
Script script = cx.compileString(scriptText, "<command>", 1, null);
if (script != null) {
script.exec(cx, getShellScope()); // <- just an eval
}
Из трех оболочек Rhino - это тот, который делает самое близкое к фактическому eval
без какой-либо обертки. Rhino ближе всего к фактическому оператору eval()
, и вы можете ожидать, что он будет вести себя точно так же, как eval
.