Pourquoi [1,2] + [3,4] = "1,23,4" en JavaScript?

405

Je voulais ajouter les éléments d'un tableau dans un autre, j'ai donc essayé ceci:

[1,2] + [3,4]

Il a répondu par:

"1,23,4"

Que se passe-t-il?

okeen
la source
1
Voici une question liée à ce sujet: stackoverflow.com/questions/1724255/why-does-2-2-in-javascript
Xavi
29
Ah-ha-ha, l'intervieweur sadique peut même demander quelque chose comme - à quoi cela va retourner [1,2] + [5,6,7] [1,2]. Pourquoi?
shabunc
9
Je pense que [1,2] + [3,4] a été l'expression la plus évaluée dans Firebug cette semaine, après alerte («merde»).
okeen
6
Veux rire ? Essayez [] + [], {} + [], {} + {} et [] + {}
Intrepidd
1
@shabunc - soin d'expliquer pourquoi [5,6,7][1,2]est 7parce qu'il utilise le dernier élément dans le second tableau. Oo
vsync

Réponses:

518

L' +opérateur n'est pas défini pour les tableaux .

Ce qui se passe, c'est que Javascript convertit les tableaux en chaînes et les concatène.

 

Mise à jour

Étant donné que cette question et, par conséquent, ma réponse suscitent beaucoup d'attention, j'ai pensé qu'il serait utile et pertinent d'avoir également une vue d' ensemble du +comportement de l' opérateur en général.

Alors, c'est parti.

À l'exception d'E4X et des éléments spécifiques à l'implémentation, Javascript (à partir d'ES5) dispose de 6 types de données intégrés :

  1. Indéfini
  2. Nul
  3. Booléen
  4. Nombre
  5. Chaîne
  6. Objet

Notez que bien que renvoyant de façon typeof quelque peu confuse object pour Null et functionpour les objets appelables, Null n'est en fait pas un objet et à proprement parler, dans les implémentations Javascript conformes aux spécifications, toutes les fonctions sont considérées comme des objets.

C'est vrai - Javascript n'a pas de tableaux primitifs en tant que tels; seules les instances d'un objet appelé Arrayavec du sucre syntaxique pour soulager la douleur.

Pour ajouter à la confusion, les entités wrapper telles que new Number(5), new Boolean(true)et new String("abc")sont toutes de objecttype, pas des nombres, des booléens ou des chaînes comme on pourrait s'y attendre. Néanmoins pour les opérateurs arithmétiques Numberet Booleanse comportent comme des nombres.

Facile, hein? Avec tout cela à l'écart, nous pouvons passer à l'aperçu lui-même.

Différents types de résultats par types d' +opérande

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

* s'applique à Chrome13, FF6, Opera11 et IE9. La vérification d'autres navigateurs et versions reste un exercice pour le lecteur.

Note: Comme l' a souligné CMS , pour certains cas d'objets tels que Number, Booleanet ceux personnalisés , l' +opérateur ne produit pas nécessairement un résultat de chaîne. Il peut varier en fonction de l'implémentation de la conversion d'objet en primitif. Par exemple, var o = { valueOf:function () { return 4; } };évaluer les o + 2;produits 6, a number, évaluer les o + '2'produits '42', a string.

Pour voir comment le tableau de synthèse a été généré, visitez http://jsfiddle.net/1obxuc7m/

Saul
la source
244

L' +opérateur de JavaScript a deux objectifs: ajouter deux nombres ou joindre deux chaînes. Il n'a pas de comportement spécifique pour les tableaux, il les convertit donc en chaînes, puis les joint.

Si vous souhaitez joindre deux tableaux pour en produire un nouveau, utilisez plutôt la .concatméthode :

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

Si vous souhaitez ajouter efficacement tous les éléments d'un tableau à un autre, vous devez utiliser la méthode .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]

Le comportement de l' +opérateur est défini dans ECMA-262 5e Section 11.6.1 :

11.6.1 L'opérateur Addition (+)

L'opérateur d'addition effectue une concaténation de chaînes ou une addition numérique. La production AdditiveExpression : AdditiveExpression + MultiplicativeExpressionest évaluée comme suit:

  1. Soit lrefle résultat de l'évaluation AdditiveExpression.
  2. Laissez - lvalêtre GetValue(lref).
  3. Soit rrefle résultat de l'évaluation MultiplicativeExpression.
  4. Laissez - rvalêtre GetValue(rref).
  5. Laissez - lprimêtre ToPrimitive(lval).
  6. Laissez - rprimêtre ToPrimitive(rval).
  7. Si Type(lprim)est Stringou Type(rprim)est String, alors
    1. Renvoie la chaîne résultant de la concaténation ToString(lprim)suivie deToString(rprim)
  8. Renvoie le résultat de l'application de l'opération d'addition à ToNumber(lprim)et ToNumber(rprim). Voir la note ci-dessous 11.6.3.

Vous pouvez voir que chaque opérande est converti ToPrimitive. En lisant plus loin, nous pouvons trouver que ToPrimitiveconvertira toujours les tableaux en chaînes, produisant ce résultat.

Jeremy Banks
la source
7
+1 car cette réponse explique non seulement le problème, mais explique également comment le faire correctement.
schnaader
3
il y a un petit tmi ici, mais je suis d'accord avec schnaader. Les meilleures réponses expliquent le problème / l'erreur / le comportement qui est demandé, puis montrent comment produire le résultat souhaité. +1
matthewdunnam
1
Pourquoi utiliseriez-vous le plus verbeux Array.prototype.push.apply(data, [3, 4])au lieu de data.concat([3,4])?
evilcelery
5
@evilcelery: Ils servent à des fins différentes. concatproduit un nouveau tableau, l'appel plus long étend efficacement un tableau existant .
Jeremy Banks
1
Vous pouvez utiliser [].push.apply(data, [3,4])pour un peu moins de verbosité. En outre, cela est garanti résistant aux autres personnes qui modifient la valeur de Array.
Sam Tobin-Hochstadt
43

Il ajoute les deux tableaux comme s'il s'agissait de chaînes .

La représentation sous forme de chaîne pour le premier tableau serait "1,2" et la seconde serait "3,4" . Ainsi, lorsque le +signe est trouvé, il ne peut pas additionner des tableaux, puis les concaténer comme étant des chaînes.

Doug
la source
Oui, c'est la première et unique explication qui vient à l'esprit, mais n'est-ce pas un comportement très étrange? peut-être qu'il y a une opération / transformation sombre et inconnue en cours, et j'aimerais connaître les détails: P
okeen
40

Les +chaînes de concats, donc il convertit les tableaux en chaînes.

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

Pour combiner des tableaux, utilisez concat.

[1,2].concat([3,4])
[1,2,3,4]
Rocket Hazmat
la source
21

En JavaScript, l'opérateur d'addition binaire ( +) effectue à la fois l'addition numérique et la concaténation de chaînes. Cependant, lorsque son premier argument n'est ni un nombre ni une chaîne, il le convertit en chaîne (d'où " 1,2"), puis il fait de même avec le second " 3,4" et les concatène en " 1,23,4".

Essayez d'utiliser la méthode "concat" des tableaux à la place:

var a = [1, 2];
var b = [3, 4];
a.concat(b) ; // => [1, 2, 3, 4];
maerics
la source
19

Il s'agit de convertir les tableaux individuels en chaînes, puis de combiner les chaînes.

tadman
la source
14

Il semble que JavaScript transforme vos tableaux en chaînes et les relie ensemble. Si vous souhaitez ajouter des tuples ensemble, vous devrez utiliser une boucle ou une fonction de carte.

Adam Fabicki
la source
14

[1,2]+[3,4] en JavaScript revient à évaluer:

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

et donc pour résoudre votre problème, le mieux serait d'ajouter deux tableaux en place ou sans créer un nouveau tableau:

var a=[1,2];
var b=[3,4];
a.push.apply(a, b);
user286806
la source
12

Il fait exactement ce que vous lui avez demandé de faire.

Ce que vous ajoutez ensemble, ce sont des références de tableau (que JS convertit en chaînes), pas des nombres comme il semble. C'est un peu comme ajouter des chaînes ensemble: "hello " + "world"="hello world"

Jamie Dixon
la source
5
hehe, il fait TOUJOURS ce que j'ai demandé. Le problème est de poser la bonne question. Ce qui m'intrigue, c'est l'interprétation toString () des tableaux lorsque vous les ajoutez.
okeen
8

C'est parce que, l'opérateur + suppose que les opérandes sont des chaînes, s'ils ne sont pas des nombres. Donc, il les convertit d'abord en chaîne et en concats pour donner le résultat final, si ce n'est pas un nombre. En outre, il ne prend pas en charge les tableaux.

Prashant Singh
la source
2
L'opérateur + ne peut PAS supposer que les opérandes sont des chaînes, car 1 + 1 == 2, entre autres. C'est parce que «+» n'est pas défini pour les tableaux, donc il les chaîne.
okeen
0

Certaines réponses ici ont expliqué comment la sortie indésirable inattendue ('1,23,4' ) se produit et certaines ont expliqué comment obtenir ce qu'elles supposent être la sortie souhaitée ( [1,2,3,4]) attendue , c'est-à-dire la concaténation de tableaux. Cependant, la nature de la sortie souhaitée attendue est en fait quelque peu ambiguë car la question d'origine indique simplement "Je voulais ajouter les éléments d'un tableau dans un autre ...". Cela pourrait signifier la concaténation de tableau mais cela pourrait aussi signifier l'ajout de tuple (par exemple ici et ici ), c'est-à-dire en ajoutant les valeurs scalaires des éléments d'un tableau aux valeurs scalaires des éléments correspondants dans le second, par exemple en combinant [1,2]et [3,4]en obtenant [4,6].

En supposant que les deux tableaux ont la même arité / longueur, voici une solution simple:

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]

Andrew Willems
la source
-1

Un autre résultat utilisant simplement un simple signe "+" sera:

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

Donc, quelque chose comme ça devrait fonctionner (mais!):

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

... mais il convertira la variable a d'un tableau en chaîne! Garde le en mémoire.

Point noir
la source