Почему [1,2] + [3,4] = «1,23,4» в JavaScript?

405

Я хотел добавить элементы массива в другой, поэтому я попробовал это:

[1,2] + [3,4]

Он ответил:

"1,23,4"

Что здесь происходит?

okeen
источник
1
Вот вопрос, связанный с этой темой: stackoverflow.com/questions/1724255/why-does-2-2-in-javascript
Хави
29
Ах-ха-ха, интервьюер-садист может спросить даже что-то вроде - что это вернет [1,2] + [5,6,7] [1,2]. Почему?
Шабун
9
Я думаю, [1,2] + [3,4] было самым оцененным выражением в firebug на этой неделе, после оповещения («дерьмо»).
Океен
6
Хочу смеяться ? Попробуйте [] + [], {} + [], {} + {} и [] + {}
Intrepidd
1
@shabunc - попытайтесь объяснить, почему [5,6,7][1,2], 7потому что он использует последний элемент во втором массиве. Oo
vsync

Ответы:

518

+Оператор не определен для массивов .

В результате Javascript преобразует массивы в строки и объединяет их.

 

Обновить

Поскольку этот вопрос и, следовательно, мой ответ привлекают к себе много внимания, я подумал, что было бы полезно и уместно иметь представление о том, как +оператор ведет себя в целом.

Итак, вот и все.

За исключением E4X и специфичных для реализации вещей, Javascript (начиная с ES5) имеет 6 встроенных типов данных :

  1. Неопределенный
  2. Значение NULL
  3. логический
  4. Число
  5. строка
  6. объект

Обратите внимание на то, что хотя это typeof несколько сбивает с толку возвратов object для Null и functionдля вызываемых объектов, Null на самом деле не является объектом, и, строго говоря, в реализациях Javascript, соответствующих спецификации, все функции считаются объектами.

Это верно - у Javascript нет примитивных массивов как таковых; только экземпляры объекта, вызванные Arrayс каким-то синтаксическим сахаром, чтобы облегчить боль.

Добавляя больше к путанице, объекты обертки, такие как new Number(5), new Boolean(true)и new String("abc")все имеют objectтип, а не числа, логические значения или строки, как можно было бы ожидать. Тем не менее для арифметических операторов Numberи Booleanведут себя как числа.

Полегче, а? После всего этого мы можем перейти к самому обзору.

Различные типы результатов по типам +операндов

            || undefined | null   | boolean | number | string | object |
=========================================================================
 undefined  || number    | number | number  | number | string | string | 
 null       || number    | number | number  | number | string | string | 
 boolean    || number    | number | number  | number | string | string | 
 number     || number    | number | number  | number | string | string | 
 string     || string    | string | string  | string | string | string | 
 object     || string    | string | string  | string | string | string | 

* относится к Chrome13, FF6, Opera11 и IE9. Проверка других браузеров и версий оставлена ​​в качестве упражнения для читателя.

Примечание: Как указал CMS , для некоторых случаев таких объектов, как Number, Booleanи пользовательских из них в +операторе не обязательно производить результирующую строку. Это может варьироваться в зависимости от реализации преобразования объекта в примитив. Например, var o = { valueOf:function () { return 4; } };оценка o + 2;производит 6, а number, оценивает o + '2'производит '42', а string.

Чтобы увидеть, как была создана таблица обзора, посетите http://jsfiddle.net/1obxuc7m/

Сол
источник
244

+Оператор JavaScript имеет две цели: добавление двух чисел или объединение двух строк. Он не имеет определенного поведения для массивов, поэтому он конвертирует их в строки и затем соединяет их.

Если вы хотите , чтобы объединить два массива , чтобы произвести новый, использовать в .concatметод вместо этого:

[1, 2].concat([3, 4]) // [1, 2, 3, 4]

Если вы хотите эффективно добавить все элементы из одного массива в другой, вам нужно использовать метод .push :

var data = [1, 2];

// ES6+:
data.push(...[3, 4]);
// or legacy:
Array.prototype.push.apply(data, [3, 4]);

// data is now [1, 2, 3, 4]

Поведение +оператора определено в ECMA-262 5e Раздел 11.6.1 :

11.6.1 Оператор сложения (+)

Оператор сложения выполняет либо конкатенацию строк, либо числовое сложение. Производство AdditiveExpression : AdditiveExpression + MultiplicativeExpressionоценивается следующим образом:

  1. Позвольте lrefбыть результатом оценки AdditiveExpression.
  2. Позвольте lvalбыть GetValue(lref).
  3. Позвольте rrefбыть результатом оценки MultiplicativeExpression.
  4. Позвольте rvalбыть GetValue(rref).
  5. Позвольте lprimбыть ToPrimitive(lval).
  6. Позвольте rprimбыть ToPrimitive(rval).
  7. Если Type(lprim)есть Stringили Type(rprim)есть String, то
    1. Вернуть строку, которая является результатом конкатенации с ToString(lprim)последующимToString(rprim)
  8. Вернуть результат применения операции сложения к ToNumber(lprim)и ToNumber(rprim). См. Примечание ниже 11.6.3.

Вы можете видеть, что каждый операнд конвертируется ToPrimitive. Читая дальше, мы можем найти, что ToPrimitiveвсегда будут преобразовывать массивы в строки, что приведет к такому результату.

Джереми Бэнкс
источник
7
+1, так как этот ответ не только объясняет проблему, но и объясняет, как сделать это правильно.
шнаадер
3
здесь немного tmi, но я согласен с schnaader. Лучшие ответы объясняют проблему / ошибку / поведение, о которой спрашивают, а затем показывают, как получить ожидаемый результат. +1
Мэтьюдуннам
1
Почему вы используете более многословный Array.prototype.push.apply(data, [3, 4])вместо data.concat([3,4])?
evilcelery
5
@evilcelery: они служат разным целям. concatсоздает новый массив, более длительный вызов эффективно расширяет существующий массив.
Джереми Бэнкс
1
Вы можете использовать [].push.apply(data, [3,4])для немного меньше многословия. Кроме того, это гарантированно устойчиво к другим людям, меняющим значение Array.
Сэм Тобин-Хохштадт
43

Он добавляет два массива, как если бы они были строками .

Строковое представление для первого массива будет «1,2», а для второго - «3,4» . Поэтому, когда +знак найден, он не может суммировать массивы, а затем объединять их как строки.

Doug
источник
Да, это первое и уникальное объяснение, приходящее на ум, но разве это не очень странное поведение? может быть, происходит какая-то темная, неизвестная операция / трансформация, и я хотел бы знать, что происходит: P
okeen
40

В +concats строки, таким образом он преобразует массивы строк.

[1,2] + [3,4]
'1,2' + '3,4'
1,23,4

Чтобы объединить массивы, используйте concat.

[1,2].concat([3,4])
[1,2,3,4]
Ракета Хазмат
источник
21

В JavaScript оператор двоичного сложения ( +) выполняет как числовое сложение, так и конкатенацию строк. Однако, когда его первый аргумент не является ни числом, ни строкой, он преобразует его в строку (следовательно, " 1,2"), затем он делает то же самое со вторым " 3,4" и объединяет их в " 1,23,4".

Попробуйте вместо этого использовать метод concat для массивов:

var a = [1, 2];
var b = [3, 4];
a.concat(b) ; // => [1, 2, 3, 4];
maerics
источник
19

Это преобразование отдельных массивов в строки, а затем объединение строк.

Тадман
источник
14

Похоже, JavaScript превращает ваши массивы в строки и соединяет их вместе. Если вы хотите добавить кортежи вместе, вам придется использовать цикл или функцию карты.

Адам Фабицки
источник
14

[1,2]+[3,4] в JavaScript это то же самое, что и оценка:

new Array( [1,2] ).toString() + new Array( [3,4] ).toString();

и поэтому, чтобы решить вашу проблему, лучше всего было бы добавить два массива на месте или без создания нового массива:

var a=[1,2];
var b=[3,4];
a.push.apply(a, b);
user286806
источник
12

Он делает именно то, что вы просили.

То, что вы добавляете вместе, это ссылки на массивы (которые JS преобразует в строки), а не числа, как кажется. Это немного похоже на сложение строк: "hello " + "world"="hello world"

Джейми Диксон
источник
5
хе-хе, он всегда делает то, что я просил. Проблема в том, чтобы задать хороший вопрос. Что меня интригует, так это интерпретация массивов методом toString () при их добавлении.
okeen
8

было бы неплохо, если бы вы могли перегрузить операторы в JavaScript, но не можете: Могу ли я определить пользовательские перегрузки операторов в Javascript? вы можете взломать только оператор "==", который преобразует в строки перед сравнением: http://blogger.xs4all.nl/peterned/archive/2009/04/01/462517.aspx

Джордж Бирбилис
источник
8

Это потому, что оператор + предполагает, что операнды являются строковыми, если они не являются числами. Таким образом, он сначала преобразует их в строку и конкататы, чтобы получить конечный результат, если это не число. Кроме того, он не поддерживает массивы.

Прашант Сингх
источник
2
Оператор + НЕ МОЖЕТ предполагать, что операнды являются строками, потому что 1 + 1 == 2, среди прочего. Это связано с тем, что «+» не определен для массивов, поэтому он должен их интерпретировать.
okeen
0

Некоторые ответы здесь объясняют, как происходит непредвиденный нежелательный выход ( '1,23,4'), а некоторые объясняют, как получить то, что они предполагают как ожидаемый желаемый выход ( [1,2,3,4]), то есть конкатенацию массива. Однако природа ожидаемого желаемого результата на самом деле несколько двусмысленна, потому что первоначальный вопрос просто гласит: «Я хотел добавить элементы массива в другой ...». Это может означать конкатенацию массивов, но это также может означать добавление кортежей (например, здесь и здесь ), то есть добавление скалярных значений элементов в одном массиве к скалярным значениям соответствующих элементов во втором, например, объединение [1,2]и [3,4]получение [4,6].

Предполагая, что оба массива имеют одинаковую арность / длину, вот одно простое решение:

const arr1 = [1, 2];
const arr2 = [3, 4];

const add = (a1, a2) => a1.map((e, i) => e + a2[i]);

console.log(add(arr1, arr2)); // ==> [4, 6]

Эндрю Виллемс
источник
-1

Еще один результат использования простого знака «+»:

[1,2]+','+[3,4] === [1,2,3,4]

Так что примерно так должно работать (но!)

var a=[1,2];
var b=[3,4];
a=a+','+b; // [1,2,3,4]

... но он преобразует переменную из массива в строку! Имейте это в виду.

Угорь
источник