解析 ++[[]][+[]]+[+[]] 返回字符串 "10" 的原因

解析 ++[[]][+[]]+[+[]] 返回字符串 “10” 的原因

技术背景

在 JavaScript 中,运算符和数据类型的隐式转换规则较为复杂。++[[]][+[]]+[+[]] 这个表达式就是一个很好的例子,它涉及到数组、一元运算符、前置递增运算符以及加法运算符的组合,同时伴随着多次的数据类型转换。理解这类复杂表达式的求值过程,有助于我们深入掌握 JavaScript 的语言特性。

实现步骤

1. 解析 +[]

在 JavaScript 中,+ 作为一元运算符时,会尝试将其操作数转换为数字类型。对于空数组 [],转换过程如下:

  • 首先,根据规范,+ 运算符会调用 ToNumber 方法将操作数转换为数字。对于对象类型(数组属于对象),会先调用 ToPrimitive 方法将其转换为原始值,并且提示类型为 String
  • ToPrimitive 方法会调用对象的 [[DefaultValue]] 内部方法。对于数组,[[DefaultValue]] 方法会先尝试调用 toString 方法。而数组的 toString 方法实际上是调用 join 方法,空数组调用 join 方法会返回空字符串 ""
  • 最后,ToNumber 方法将空字符串 "" 转换为数字 0,所以 +[] === 0

2. 简化表达式为 ++[[]][0] + [0]

因为 +[] 等于 0,所以原表达式 ++[[]][+[]]+[+[]] 可简化为 ++[[]][0] + [0][[]][0] 表示从数组 [[]] 中获取第一个元素,即内部的空数组 []

3. 解析 ++[[]][0]

++ 是前置递增运算符,它会先将操作数转换为数字,然后加 1,并返回递增后的结果。这里 [[]][0] 得到的空数组 [] 会被转换为数字 0,然后加 1,最终结果为 1

4. 解析 1 + [0]

当加法运算符 + 的两个操作数一个是数字,一个是数组时,会先将数组转换为字符串。数组 [0] 转换为字符串后是 "0",然后数字 1 也会被转换为字符串 "1",最后进行字符串拼接,得到 "10"

核心代码

1
2
3
4
5
6
7
8
9
// 验证 +[] 的值
console.log(+[]); // 输出: 0

// 验证 ++[[]][0] 的值
const arr = [[]];
console.log(++arr[0]); // 输出: 1

// 验证 1 + [0] 的结果
console.log(1 + [0]); // 输出: "10"

最佳实践

虽然这类复杂的表达式在实际开发中很少使用,但理解其求值过程有助于我们编写更健壮的代码。在实际开发中,应尽量避免使用过于复杂的隐式类型转换,明确指定数据类型的转换,以提高代码的可读性和可维护性。例如:

1
2
3
4
5
// 明确的类型转换
const num1 = 1;
const num2 = 0;
const result = String(num1) + String(num2);
console.log(result); // 输出: "10"

常见问题

1. 为什么 ++[] 会报错?

++ 运算符用于递增变量或对象属性的值,它不仅要计算新值,还要将新值存储回去。而 ++[] 中,空数组没有可以存储新值的引用,因此会报错。

2. 如何改变表达式的结果?

如果重写 Array.prototypetoString()valueOf() 方法,会改变对象转换为原始值的结果,从而改变表达式的最终结果。例如:

1
2
3
4
Array.prototype.toString = function() {
return "foo";
};
console.log(++[[]][+[]]+[+[]]); // 输出: "NaNfoo"

解析 ++[[]][+[]]+[+[]] 返回字符串 "10" 的原因
https://119291.xyz/posts/explanation-of-plus-plus-array-expression/
作者
ww
发布于
2025年5月26日
许可协议