Почему сложение числа с массивом выдает строку?

Как видно из примера ниже, массив и число складываются как строки.

console.log([5] + 25); //525

Почему так происходит? При выполнении сложения для массива запускается метод toString? Разве метод toString не должен запускаться только при попытке преобразования объекта к строке (String, alert...)? А в этом случае я не пытаюсь преобразовать массив к строке...


Ответы (1 шт):

Автор решения: Leonid

Согласно Флэнагану (JavaScript. Полное руководство, 7-е издание - 2021):

Операция + в JavaScript выполняет сложение чисел и конкатенацию строк. Если любой из ее операндов оказывается объектом, то интерпретатор JavaScript преобразует его в элементарное значение с применением алгоритма no-preference. После получения двух элементарных значений проверяются их типы. Если один из аргументов является строкой, тогда другой преобразовывается в строку и выполняется конкатенация строк.

То есть, массив преобразуется в строку, почему:

Алгоритм no-preference зависит от класса преобразуемого объекта. Если объект относится к классу Date, тогда интерпретатор JavaScript применяет алгоритм prefer-string. Для любого другого объекта интерпретатор JavaScript использует агоритм prefer-number.

Алгоритм prefer-number первым испытывает valueOf(), а вторым - toString().

(Если valueOf() не существует или возвращает объект). Другими словами, массив должен бы преобразоваться в число, НО:

Класс Array наследует стандартный метод valueOf(), который не возвращает элементарное значение. Таким образом, когда мы пытаемся преобразовать массив в число, то в итоге получаем вызов метода toString() массива.

Массив с единственным элементом преобразуется в ту же строку, что и этот один элемент. Если массив содержит единственное число, то оно преобразуется в строку.

Таким образом, массив, в силу того, что его метод valueOf() возвращает сам массив как объект, в итоге возвращает результат вызова toString().

Для эксперимента можно переписать метод valueOf():

Array.prototype.valueOf = function(){
    return Number(this[0]);
}
console.log([5] + 25);

То, что возвращает родной valueOf() для сравнения:

console.log([5].valueOf());

→ Ссылка