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