Добавление на два пустых объекта или пустые массивы в javascript

Я просто играл с javascript, когда нашел следующие выходы консоли:

  • [] + []//вывод: ""
  • [] + {}//вывод: [object Object]
  • {} + []//вывод: 0
  • {} + {}//вывод: NaN

Может ли кто-нибудь объяснить мне логику этих результатов. Я чувствую, что это очень странное поведение, но, наверное, у него есть логика.

Спасибо заранее.

Ответ 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(). Но это также не так, как использование ввода в качестве операторов в программах.

Ссылки

Что такое {} + {} в JavaScript?