Почему {} + {} NaN только на стороне клиента? Почему не в Node.js?

136

Пока [] + []пустая строка, [] + {}есть "[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)
Ионика Бизэу
источник
4
Это просто консоль браузера, которая делает это. Попробуйте войти в консоль , и это то же самое , как и в NodeJS. jsbin.com/oveyuj/1/edit
elclanrs
2
Не совсем дубликат, я прошу ответ NodeJS. Голосование за возобновление ...
Ionică Bizau
4
Хм ... прости. Тем не менее, stackoverflow.com/questions/9032856/… все еще актуален и отвечает на первую половину
Джон Дворжак
3
Не забывайте, что {}это можно интерпретировать как выражение или как примитив объекта в зависимости от контекста. Может быть, код одинаков на клиенте и на сервере, но интерпретируется по- {}разному из-за разного контекста ввода кода.
Паташу
18
Пожалуйста, откройте и затем прекратите закрывать этот вопрос снова и снова, так как этот вопрос на самом деле не является дубликатом .
Элвин Вонг

Ответы:

132

Обновлено примечание: это было исправлено в 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.

  • Это поражает runметод IProxy .

  • Это вызывает evalInlineScript( SRC ). Это просто компилирует строку и уничтожает ее.

В принципе:

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

Из всех трех оболочка Rhino - это та, которая ближе всего подходит к реальному evalбез какой-либо обертки. Rhino's наиболее близок к реальному eval()утверждению, и вы можете ожидать, что оно будет вести себя точно так же, как и evalраньше.

Бенджамин Грюнбаум
источник
1
(Не совсем часть ответа, но стоит упомянуть, что nodejs использует модуль vm для оценки по умолчанию при использовании REPL, а не только JavaScript eval)
Бенджамин Грюнбаум
Можете ли вы объяснить, почему Rhino , например, делает то же самое в терминале (не только в Chrome Console)?
Ионика Бизэ
5
+10 если бы было возможно! Ух ты, чувак ... У тебя действительно нет жизни или ты действительно умнее меня, чтобы знать что-то подобное. Пожалуйста, скажите мне, что вы искали немного, чтобы найти этот ответ :)
Самуил
7
@ Самуэль Все, что нужно, это прочитать источник - клянусь! В Chrome, если вы введете «отладчик»; , вы получите всю трубу - она ​​переместит вас прямо в 'с' с помощью только одной функции выше evaluateOn. В ноде все очень хорошо документировано - у них есть специальный модуль REPL со всей историей, приятной и удобной на git, раньше я использовал REPL в своих собственных программах, я знал, где искать :) Я рад, что вам понравилось и нашел это полезно, но я обязан своим знакомством с этими основами кода (dev-tools и nodejs), а не своим интеллектом. Переход к источнику часто всегда самый простой.
Бенджамин Грюнбаум
Обновление - API консоли в Chrome немного обновлен, поэтому, хотя общая идея здесь верна, опубликованный код не является точным для самой последней версии Chrome. См. Chromium.googlesource.com/chromium/blink.git/+/master/Source/…
Бенджамин Грюнбаум