为什么JavaScript中[1,2] + [3,4]的结果是"1,23,4"?

416

我想将一个数组的元素添加到另一个数组中,所以我尝试了这个:

[1,2] + [3,4]

它做出了回应:

"1,23,4"

发生了什么?


30
啊哈哈,虐待狂的面试官甚至可以问像这样的问题 - [1,2] + [5,6,7][1,2] 会返回什么。为什么? - shabunc
9
在Firebug中,自alert('crap')之后,我认为[1,2] + [3,4]是最经常被评估的表达式。 - okeen
6
想发笑吗?试试 [] + [], {} + [], {} + {} 和 [] + {}。 - Intrepidd
1
@shabunc - 能否解释一下为什么 [5,6,7][1,2] 的结果是 7,因为它使用了第二个数组中的最后一个元素。O.o - vsync
1
@vsync - 我不太擅长使用“care to explain”这个结构,因为我不是母语使用者。所以,你基本上是在问为什么?因为在“取索引”上下文中,逗号分隔值列表解析为最后一个。 - shabunc
显示剩余3条评论
14个回答

530

+ 运算符不适用于数组。

发生的情况是 Javascript 将数组转换为字符串并连接它们。

 

更新

由于这个问题及我的回答引起了很多关注,我认为有一个关于+运算符一般行为的概述也会很有用和相关。

所以,接下来就是它。

除了E4X和特定实现的东西外,Javascript(截至ES5)有6个内置数据类型

  1. 未定义
  2. 布尔
  3. 数字
  4. 字符串
  5. 对象

请注意,尽管typeof有点令人困惑地返回object用于Null和function用于可调用对象,但Null实际上不是一个对象,严格来说,在规范符合的Javascript实现中,所有函数都被认为是对象。

没错- Javascript没有原始数组,只有一个名为Array的对象实例,其中一些语法糖可以减轻痛苦。

为了增加混淆,包装实体比如 new Number(5)new Boolean(true)new String("abc") 都是 object 类型,而不是数字、布尔值或字符串,这可能会让人感到意外。但是对于算术运算符来说,NumberBoolean 会像数字一样行事。

很简单,对吧?现在我们可以继续进行概述了。

操作数类型不同导致的 + 运算结果类型不同

            || 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所指出的那样,对于某些对象(如NumberBoolean和自定义对象),+运算符不一定产生一个字符串结果。它可能会因对象到原始值转换的实现而异。例如,var o = { valueOf:function () { return 4; } };评估o + 2;会产生6,一个number,评估o + '2'会产生'42',一个string

要了解概述表是如何生成的,请访问http://jsfiddle.net/1obxuc7m/


如果您将两个Date对象相加会发生什么?typeof new Date()返回"object"。根据您的表格,添加结果应该是一个字符串,但事实并非如此。它是一个数字。加法运算符肯定存在更多细节,而不仅仅是表格中所显示的那些。它在每种情况下都无效。 - user15321167
@user15321167 打开开发者控制台,输入 typeof(new Date() + new Date()) 并按回车键。结果将是 string,除非您使用的浏览器运行非标准的 JavaScript 实现。该表格是有效的。 - Saul

248

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. 计算 AdditiveExpression 生成引用 lref
  2. 计算 GetValue(lref) 并将结果赋值给 lval
  3. 计算 MultiplicativeExpression 生成引用 rref
  4. 计算GetValue(rref) 并将结果赋值给 rval
  5. lval 转换为原始值 lprim
  6. rval 转换为原始值 rprim
  7. 如果 Type(lprim) 是字符串或者 Type(rprim) 是字符串,那么
    1. 返回由将 ToString(lprim)ToString(rprim) 相连得到的字符串。
  8. 否则,返回将 ToNumber(lprim)ToNumber(rprim) 的结果应用于加法运算符的值。参见11.6.3的注释。

可以看到每个操作数都被转换为了原始值(ToPrimitive)。通过继续阅读,我们可以发现ToPrimitive总是将数组转换为字符串,从而产生这个结果。


8
+1,因为这个回答不仅解释了问题,还解释了如何正确地做。 - schnaader
4
这里有点过于详细了,但我同意schnaader的看法。最好的答案是先解释所询问的问题/错误/行为,然后展示如何得出预期的结果。+1 - matthewdunnam
1
为什么要使用更冗长的 Array.prototype.push.apply(data, [3, 4]) 而不是 data.concat([3,4]) - evilcelery
5
它们有不同的用途。concat生成一个的数组,而更长的调用则有效地扩展了现有的数组。 - Jeremy
1
你可以使用[].push.apply(data, [3,4])来减少冗余。此外,这也保证了对于其他人更改Array的值是有抵抗力的。 - Sam Tobin-Hochstadt

43

它将这两个数组添加为如果它们是字符串

第一个数组的字符串表示将是"1,2",第二个将是"3,4"。因此,当找到+符号时,它不能将数组相加,然后将它们连接作为字符串。


是的,这是脑海中第一个也是唯一的解释,但这不是非常奇怪的行为吗?也许有一些神秘的、未知的操作/转换正在进行,我很想知道其中的内部细节:P - okeen

40

+ 连接字符串,因此它将数组转换为字符串。

[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中的[1,2] + [3,4]等价于以下表达式:

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

为了解决你的问题,最好的方法是原地添加两个数组,或者不创建新的数组:

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

14

看起来JavaScript将您的数组转换为字符串并将它们连接在一起。如果要将元组相加,则必须使用循环或map函数。


12

它正在按照您要求的方式执行。

您正在相加的是数组引用(JS会将其转换为字符串),而不是像看起来那样的数字。 这有点像将字符串相加:"hello " + "world" = "hello world"


5
嘿嘿,它总是按照我的要求去做。问题在于如何提出好的问题。引起我的兴趣的是当你将数组相加时,toString()方法是如何解释它们的。 - okeen

8
由于 + 操作符假设操作数是字符串,如果它们不是数字,则会将它们首先转换为字符串并连接起来以给出最终结果。此外,它不支持数组。

2
加号运算符不能假定操作数是字符串,因为1 + 1 == 2等等。这是因为“+”未定义数组,所以它将其toString。 - okeen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接