Ответ 1
Ожидаемые результаты
Когда вы добавляете два массива, все работает так, как ожидалось:
[] + []//output''
Преобразование []
в примитив сначала пытается valueOf()
, который возвращает сам массив (this
):
var arr = [];
arr.valueOf() === arr
true
Поскольку этот результат не является примитивным, toString() вызывается следующим и возвращает пустую строку (которая является примитивной). Следовательно, результатом [] + []
является конкатенация двух пустых строк.
{} + [] // output: 0
Добавление массива и объекта также соответствует нашим ожиданиям:
[] + {}//output '[object Object]'
Объяснение: преобразование пустого объекта в строку приводит к следующему результату.
String({})//output: '[object Object]'
Таким образом, предыдущий результат создается путем конкатенации ""
и "[object Object]"
.
Неожиданные результаты
Вещи становятся странными, если первый операнд + - пустой литерал объекта (результаты отображаются на консоли Firefox):
{} + {}//output: NaN
Что здесь происходит? Проблема в том, что JavaScript интерпретирует первый {}
как пустой блок кода и игнорирует его. Поэтому NaN
вычисляется путем вычисления +{}
(плюс с последующим вторым {}
). Плюс, который вы видите здесь, не является бинарным оператором сложения, а унарным префиксным оператором, который преобразует его операнд в число, таким же образом, как Number()
. Например:
+"3.65"
3.65
Следующие выражения эквивалентны:
+{}
Number({})
Number({}.toString()) // {}.valueOf() isn’t primitive
Number("[object Object]")
NaN
Почему первый {}
интерпретируется как кодовый блок? Поскольку полный ввод анализируется как оператор, а фигурные скобки в начале выражения интерпретируются как начало кода. Следовательно, вы можете исправить вещи, заставляя ввод анализироваться как выражение:
({} + {})//output: '[object Object][object Object]'
Аргументы функций или методов также всегда анализируются как выражения:
console.log({} + {})//output: [object Object][object Object]
После предыдущих объяснений вы не должны удивляться следующему результату:
{} + []//output: 0
Опять же, это интерпретируется как блок кода, за которым следует +[]
. Следующие выражения эквивалентны:
+[]
Number([])
Number([].toString()) // [].valueOf() isn’t primitive
Number("")
0
Интересно, что Node.js REPL анализирует свой ввод иначе, чем Firefox или Chrome (который даже использует тот же движок JavaScript V8 как Node.js). Следующий вход анализируется как выражение, и результаты менее удивительны:
{} + {}//output: '[object Object][object Object]'
{} + []//output '[object Object]'
Это имеет то преимущество, что вы больше похожи на результаты, которые вы получаете при использовании ввода в качестве аргументов console.log(). Но это также не так, как использование ввода в качестве операторов в программах.
Ссылки