为什么[1,2]+[3,4]=“1,23,4”;在JavaScript中?

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

[1,2] + [3,4]

它的答复是:

"1,23,4"

这是怎么回事?

46068 次浏览

它将两个数组相加,就好像它们是字符串

第一个数组的字符串表示形式是“1、2”,第二个数组的字符串表示形式是“3、4”。因此,当找到+符号时,它不能对数组求和,然后将它们作为字符串连接起来。

这完全是你让它做的。

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

+操作符不是为数组定义的

所发生的是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。检查其他浏览器和版本留给读者练习。

Boolean0正如Boolean1所指出的,对于某些情况下的对象,如NumberBoolean和自定义对象,+操作符不一定会产生字符串结果。它可以根据对象到原语转换的实现而变化。例如,var o = { valueOf:function () { return 4; } };求值o + 2;会得到6number,求值o + '2'会得到'42'string

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

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

它将单个数组转换为字符串,然后组合字符串。

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. lrefAdditiveExpression求值的结果。
  2. lvalGetValue(lref)
  3. rrefMultiplicativeExpression求值的结果。
  4. rvalGetValue(rref)
  5. lprimToPrimitive(lval)
  6. rprimToPrimitive(rval)
  7. 如果Type(lprim)StringType(rprim)String,则
    1. 返回由ToString(lprim)ToString(rprim)连接而成的String
    2. 李< / ol > < / >
    3. 返回对ToNumber(lprim)ToNumber(rprim)应用加法运算的结果。参见11.6.3的说明。

你可以看到每个操作数都被转换为ToPrimitive。通过进一步阅读,我们可以发现ToPrimitive将始终将数组转换为字符串,产生此结果。

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

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

要组合数组,使用concat

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

在JavaScript中,二进制加法运算符(+)执行数值加法和字符串连接。然而,当它的第一个参数既不是数字也不是字符串时,它会将其转换为字符串(因此是"1,2"),然后它会对第二个"3,4"做同样的操作,并将它们连接到"1,23,4"。

尝试使用Arrays的"concat"方法:

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

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

这是因为,+运算符假设操作数是字符串,如果它们不是数字。因此,如果它不是一个数字,它首先将它们转换为字符串和连接,以给出最终结果。此外,它不支持数组。

另一个使用简单的“+”符号的结果是:

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

所以像这样的东西应该工作(但是!):

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

... 但它会将变量a从数组转换为字符串!记住这一点。

这里的一些答案解释了意外的不受欢迎的输出('1,23,4')是如何发生的,还有一些解释了如何获得他们假设的预期的期望输出([1,2,3,4]),即数组连接。然而,期望输出的性质实际上有些模糊,因为最初的问题只是说“我想将一个数组的元素添加到另一个…”。可以表示数组拼接,但它可以表示元组相加(例如在这里在这里),即将一个数组中元素的标量值添加到第二个数组中相应元素的标量值,例如组合[1,2][3,4]以获得[4,6]

假设两个数组都有相同的arity/length,这里有一个简单的解决方案:

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]

由于ES6,我们可以使用展开运算符做类似于我们想做的事情:

[...[1, 2], ...[3, 4]]

给:

[1, 2, 3, 4]

它也适用于命名变量:

const a = [1, 2];
const b = [3, 4];
const array = [...a, ...b];

给出相同的结果。