Чому {} + {} є 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ă Bizău

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 роблять наступне :

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" із прикладом різниці.


Носоріг

Оновлення - ОП цікавило, як поводиться 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
}

З трьох, оболонка Носорога - це та, яка робить найбільш близьку до фактичної evalбез будь-якого обгортання. Носоріг є найближчим до фактичного eval()твердження, і ви можете очікувати, що він буде вести себе так, як evalхотів би.


1
(Насправді не частина відповіді, але варто згадати, що nodejs використовує модуль vm для evaling за замовчуванням при використанні REPL, а не лише JavaScript eval)
Бенджамін Груенбаум,

Чи можете ви пояснити, чому , наприклад, носоріг робить те саме в Terminal (не лише Chrome Console)?
Ionică Bizău

5
+10, якщо можна було! Вау людино, ... У вас насправді немає життя або ви насправді розумніші за мене, щоб знати щось подібне. Скажіть, будь ласка, що ви трохи шукали, щоб знайти цю відповідь :)
Самуель

7
@Samuel Все, що потрібно, - це читання джерела - клянусь! У Chrome, якщо ви введете "налагоджувач;" , ви отримаєте всю трубу - вона перекине вас прямо на "з", лише за допомогою однієї функції вище evaluateOn. У вузлі все дуже добре задокументовано - у них є виділений модуль REPL з усією історією, приємною та затишною на git, раніше я використовував REPLs у своїх програмах, я знав, де шукати :) Радий, що вам сподобалось і знайшли це корисно, але я завдячую своїм ознайомленням із цими базами коду (dev-tools та nodejs), а не своїм інтелектом. Їхати прямо до джерела часто завжди найпростіше.
Бенджамін Грюнбаум

Оновлення - консольний API в Chrome було оновлено трохи, тому, хоча загальна ідея тут правильна, розміщений код не є точним для останньої версії Chrome. Дивіться chromium.googlesource.com/chromium/blink.git/+/master/Source/…
Бенджамін Груенбаум
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.