Почему {} + {} - это 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)

person Ionică Bizău    schedule 24.06.2013    source источник
comment
см. также спецификацию: es5.github.io   -  person    schedule 24.06.2013
comment
@JanDvorak Спасибо! Они не говорят о NodeJS.   -  person Ionică Bizău    schedule 24.06.2013
comment
Это делает только консоль браузера. Попробуйте войти в консоль, и она такая же, как в NodeJS. jsbin.com/oveyuj/1/edit   -  person elclanrs    schedule 24.06.2013
comment
Не совсем дубликат, я прошу ответа NodeJS. Голосование за открытие ...   -  person Ionică Bizău    schedule 24.06.2013
comment
Хм ... извините. Однако stackoverflow.com/questions/9032856/, по-прежнему актуален и отвечает на первую половину   -  person John Dvorak    schedule 24.06.2013
comment
Не забывайте, что {} может интерпретироваться либо как выражение, либо как объектный примитив в зависимости от контекста. Возможно, код на клиенте и на сервере один и тот же, но он интерпретирует {} по-разному из-за разного контекста ввода кода.   -  person Patashu    schedule 24.06.2013
comment
@elclanrs Не только в браузере. Я установил rhino в терминал, который делает то же самое ({} + {} это NaN).   -  person Ionică Bizău    schedule 24.06.2013
comment
Как сказал @Patashu, это должен быть контекст. Не уверен, в чем тонкие различия во всех этих средах ...   -  person elclanrs    schedule 24.06.2013
comment
Что касается консоли node.js, я предполагаю, что это связано с тем, как она реализована. Если консоль node.js делает eval("x="+input), она будет вести себя так, как показано   -  person John Dvorak    schedule 24.06.2013
comment
@JanDvorak Это должен быть ответ, консоль узла использует this.   -  person Benjamin Gruenbaum    schedule 24.06.2013
comment
@JanDvorak Кроме того, более вероятно, что это потому, что new Function('return {}+{}')() возвращает "[object Object][object Object]". (По крайней мере, согласно документам)   -  person Benjamin Gruenbaum    schedule 24.06.2013
comment
@BenjaminGruenbaum, готовы ли вы покопаться в источниках, а затем опубликовать ответ?   -  person John Dvorak    schedule 24.06.2013
comment
Пожалуйста, снова откройте, а затем перестаньте закрывать этот вопрос снова и снова, поскольку этот вопрос действительно не повторяется.   -  person Alvin Wong    schedule 24.06.2013
comment
benjiegillam.com/2013/06/quantum-javascript отвечает на этот вопрос подробнее деталь.   -  person Mathias Bynens    schedule 26.06.2013
comment
@MathiasBynens На самом деле это меньше деталей, потому что он не объясняет почему он оценивает таким образом в nodejs (и я предполагаю, что REPL узла,) :) Спасибо за ссылку хотя.   -  person Benjamin Gruenbaum    schedule 26.06.2013
comment
@BenjaminGruenbaum Обратите внимание, что это не комментарий к вашему ответу, и это отлично!   -  person Mathias Bynens    schedule 26.06.2013
comment
... почему отрицательные голоса? :-(   -  person Ionică Bizău    schedule 24.10.2013


Ответы (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.

Node не всегда так поступал. Вот реальный коммит, изменивший его. Райан оставил следующий комментарий к изменению: «Улучшите способ преобразования команд REPL» с примером различия.


Носорог

Обновление - OP интересовался тем, как ведет себя Rhino (и почему он ведет себя как инструменты разработчика Chrome, а не nodejs).

Rhino использует совершенно другой движок JS, в отличие от инструментов разработчика Chrome и REPL Node.js, которые оба используют V8.

Вот основная конвейерная линия того, что происходит, когда вы оцениваете команду JavaScript с помощью Rhino в оболочке Rhino.

  • Оболочка запускает _8 _.

  • В свою очередь, он вызывает this new IProxy(IProxy.EVAL_INLINE_SCRIPT);, например, если код был передан напрямую с помощью встроенного переключателя -e.

  • Это попадает в run < / a> метод.

  • Он вызывает 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.

person Benjamin Gruenbaum    schedule 24.06.2013
comment
(На самом деле это не часть ответа, но стоит упомянуть, что nodejs использует модуль vm для исключения по умолчанию при использовании REPL, а не только JavaScript eval) - person Benjamin Gruenbaum; 24.06.2013
comment
Можете ли вы объяснить, почему, например, rhino делает то же самое в Терминале (а не только в консоли Chrome)? - person Ionică Bizău; 26.06.2013
comment
+10, если бы это было возможно! Вау, чувак ... У тебя действительно нет жизни, или ты действительно умнее меня, чтобы знать что-то подобное. Пожалуйста, скажите мне, что вы немного искали этот ответ :) - person Samuel; 29.06.2013
comment
@Samuel Все, что потребовалось, это прочитать источник - клянусь! В Chrome, если вы введете «отладчик;» , вы получите всю трубу - она ​​перебросит вас напрямую в 'with' с помощью только одной функции, указанной выше для evaluateOn. В node все очень хорошо документировано - у них есть специальный модуль REPL со всей историей, красивой и удобной на git, я использовал REPL раньше в своих собственных программах, я знал, где искать :) Я рад, что вам понравилось и вы нашли это полезно, но я обязан этим знанием этих кодовых баз (dev-tools и nodejs), а не своему интеллекту. Часто проще всего перейти прямо к источнику. - person Benjamin Gruenbaum; 29.06.2013
comment
Обновление - консольный API в Chrome был немного обновлен, поэтому, хотя общая идея здесь верна, опубликованный код не является точным для самой последней версии Chrome. См. chromium.googlesource.com. /chromium/blink.git/+/master/Source/ - person Benjamin Gruenbaum; 26.05.2014
comment
Почему оператор with, выполняемый глубоко в инструментах разработчика Chrome, заставляет {} оцениваться в числовом контексте? Если бы это было + {} + {}, это убедило бы меня, за исключением того, что + {} + {} - это 'Nan [object Object]' как в инструментах разработчика Chrome, так и в nodeJS. - person Paul; 01.06.2014
comment
@Paul Он запускается как Открыть новый блок кода, закрыть его, затем вычислить +{}. Выполнение +x в JavaScript точно такое же, как выполнение Number(x) в JavaScript. Таким образом, он выполняется как Number({}), что является NaN - person Benjamin Gruenbaum; 01.06.2014
comment
@BenjaminGruenbaum Я был тем, кто проголосовал против вас, потому что это выглядело не очень хорошо, но я проголосовал за него сейчас, потому что вы обновили свой ответ, чтобы он был более подробным. - person ; 03.06.2014
comment
@ İnekŞaban у вас даже не было аккаунта на момент последнего редактирования. о чем ты, черт возьми? - person rlemon; 03.06.2014
comment
@rlemon Не знаю, читали ли вы мой пост в Meta, но у меня есть привычка регулярно переключаться на новую учетную запись и IP-адрес. Но я просто хотел вернуться, чтобы посмотреть, стало ли это лучше, отсюда и положительный отзыв. - person ; 03.06.2014
comment
Я предполагаю, что ваша старая учетная запись была удалена, поэтому отрицательный голос был бы удален. - person rlemon; 03.06.2014
comment
@rlemon Я здесь с конца 2009 года, и иногда случается, что учетная запись застревает в бане (я предполагаю, потому что я делюсь этой связью со многими людьми [это общедоступная точка доступа]), и я создаю для нее новую учетную запись. Но в основном я переключаюсь только по личным причинам. - person ; 03.06.2014
comment
@ İnekŞaban ссылку на ваш старый аккаунт? - person Benjamin Gruenbaum; 03.06.2014
comment
@BenjaminGruenbaum Что ты хочешь с этим делать? В любом случае он здесь, stackoverflow.com/users/107560/caglar-toklu Если вы удалите его, вы ничего не решите , поэтому, пожалуйста, придерживайтесь конструктивной позиции. Не хочу неприятностей, и ты тоже. - person ; 03.06.2014