Чому [1,2] + [3,4] = “1,23,4” у JavaScript?


405

Я хотів додати елементи масиву в інший, тому спробував це:

[1,2] + [3,4]

Він відповів:

"1,23,4"

Що відбувається?


1
Ось питання, пов’язані з цією темою: stackoverflow.com/questions/1724255/why-does-2-2-in-javascript
Xavi

29
А-ха-ха, інтерв'юер-садист може запитати навіть щось на зразок - що це поверне [1,2] + [5,6,7] [1,2]. чому?
shabunc

9
Я думаю, що [1,2] + [3,4] був найбільш оціненим виразом у firebug на цьому тижні, після попередження ("лайно").
okeen

6
Хочете сміятися? Спробуйте [] + [], {} + [], {} + {} та [] + {}
Intrepidd

1
@shabunc - обережно пояснити, чому так [5,6,7][1,2], 7оскільки він використовує останній елемент у другому масиві. Oo
vsync

Відповіді:


518

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

Що відбувається, так що Javascript перетворює масиви в рядки і об'єднує їх.

 

Оновлення

Оскільки це питання, а отже, моя відповідь привертає багато уваги, я вважав, що було б корисно і актуально мати огляд того, як +поводиться оператор в цілому.

Отже, ось це йде.

За винятком E4X та специфічних для реалізації матеріалів, Javascript (як і ES5) має 6 вбудованих типів даних :

  1. Не визначено
  2. Нуль
  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, A number, оцінка o + '2'виробляє '42', A 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]

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

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
тут трохи тмі, але я згоден із шнадером. Найкращі відповіді пояснюють проблему / помилку / поведінку, про яку ставлять запитання, а потім показує, як отримати бажаний результат. +1
matthewdunnam

1
Чому б ви використовували більше багатослівного Array.prototype.push.apply(data, [3, 4])замість data.concat([3,4])?
злодійство

5
@evilcelery: Вони служать різним цілям. concatстворює новий масив, тим довший виклик ефективно розширює існуючий масив.
Джеремі Бенкс

1
Можна використовувати [].push.apply(data, [3,4])для дещо меншої багатослівності. Крім того, це гарантовано стійкість до інших людей, що змінюють цінність Array.
Сем Тобін-Хохштадт

43

Він додає два масиви так, ніби вони є рядками .

Представлення рядків для першого масиву буде "1,2", а для другого - "3,4" . Отже, коли +знак знайдений, він не може підсумовувати масиви, а потім об'єднувати їх як рядки.


Так, це перше і унікальне пояснення, що приходить до розуму, але хіба це не дуже дивна поведінка? можливо, робиться якась темна, невідома операція / трансформація, і я хотів би знати внутрішні: P
Окінь

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];

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);

12

Це робить саме те, що ви просили.

Що ви додаєте разом, це посилання на масив (який JS перетворює у рядки), а не числа, як здається. Це трохи схоже на додавання рядків разом: "hello " + "world"="hello world"


5
хе-х, це ВСЕГДА робить те, про що я попросив. Проблема полягає в тому, щоб поставити гарне запитання. Що мене інтригує, - інтерпретація масивів toString (), коли ви додаєте їх.
Окінь

8

Було б добре, якщо ви можете перевантажувати операторів у JavaScript, але ви не можете: Чи можна визначити власні перевантаження операторів у Javascript? ви можете зламати лише оператора "==", який перетворює на рядки перед порівнянням: http://blogger.xs4all.nl/peterned/archive/2009/04/01/462517.aspx


8

Це тому, що, + оператор припускає, що операнди є рядковими, якщо вони не є числами. Отже, він спочатку перетворює їх у рядки та конмати, щоб дати кінцевий результат, якщо його не число. Крім того, він не підтримує масиви.


2
Оператор + НЕ може вважати, що операнди є рядками, тому що 1 + 1 == 2, серед інших. Це тому, що "+" не визначено для масивів, тож це доString-s їм.
Окінь

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]

... але вона перетворить змінну змінної з масиву в рядок! Майте це на увазі.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.