先记住这个答案
+ 是 JavaScript 里唯一一个既能做加法又能做拼接的运算符。它先把两边转成原始值,只要其中一个是字符串,就把另一个也转成字符串,然后拼起来;两边都不是字符串,就都转成数字相加。多个 + 连写时从左往右一步步算,所以结果取决于字符串出现在哪个位置。
- 只要一边是字符串,
+就做拼接 - 从左到右逐步计算,前面先算出的结果决定后面
- 对象和数组先转原始值,通常变成字符串
- 减、乘、除只做数字运算,
'5' - 2是 3
位置不同,结果就不同
面试最常考的就是几个字面量混着加,关键是按顺序一步一步算,不要一眼看全。
console.log(1 + 2 + '3') // 先算 1 + 2 = 3,再和 '3' 拼接
console.log('1' + 2 + 3) // 第一步就变成 '12',后面全是拼接
console.log('a' + true, 'a' + null, 'a' + undefined)
console.log(1 + true, 1 + null, 1 + undefined)
console.log('x' + [1, 2], 'x' + {})
console.log(JSON.stringify([] + []), [] + {})
console.log('5' - 2, '5' * '2') // 只有 + 会拼接查看输出与解释
33
123
atrue anull aundefined
2 1 NaN
x1,2 x[object Object]
"" [object Object]
3 10第一行 1 + 2 两边都是数字,先得到 3,再遇到字符串才开始拼接。第二行一开头就是字符串,之后每一步左边都是字符串,所以一路拼下去。
没有字符串参与时,布尔值、null、undefined 走的是数字转换:true 是 1,null 是 0,undefined 是 NaN。一旦有字符串参与,它们就按字面变成 'true'、'null'、'undefined'。
对象和数组是怎么变成字符串的
对象参与 + 时,引擎先把它转成原始值。普通对象和数组的 valueOf 返回的还是对象本身,不是原始值,于是接着调用 toString。数组的 toString 相当于 join(','),所以 [1, 2] 变成 '1,2',空数组变成空字符串;普通对象的 toString 返回 '[object Object]'。
这就解释了 [] + [] 是空字符串、[] + {} 是 '[object Object]'。两边转完原始值后已经是字符串了,自然走拼接。
- 自定义对象的拼接结果
- 如果对象自己定义了
valueOf或toString,拼接结果就由它们决定。比如给一个金额对象写toString() { return '¥' + this.amount },那'价格:' + price就会输出带货币符号的文本。更精细的控制可以用Symbol.toPrimitive,它会收到'string'、'number'、'default'这样的提示,+传的是'default'。
项目里怎么写更稳
表单里拿到的值都是字符串。input.value + 1 得到的是 '101' 这种结果,而不是 11,这是线上很常见的 bug。求和前先用 Number() 或一元加号转成数字,再相加。
拼接展示文本时,优先用模板字符串,把变量写成 ${count} 插进去。它明确表达「我要拼字符串」,里面的表达式会先按原本的类型算完,再整体转成字符串,读的人不用去推 + 的顺序。
容易答错的地方
- 以为
+会优先做数字加法 - 有人觉得
'1' + 2会把'1'转成数字得到 3。实际规则正好相反,有字符串就拼接。只有减号、乘号、除号、取余这些运算符才会把字符串转成数字,所以'5' - 2是 3,'5' + 2是'52'。 - 认为
null拼接后是空字符串 'a' + null是'anull','a' + undefined是'aundefined'。页面上出现「用户名undefined」通常就是这个原因。需要兜底时写name ?? '',不要指望拼接自己吞掉空值。- 把
{} + []当成表达式来算 - 在控制台直接敲
{} + [],开头的{}会被当成空代码块,实际执行的是+[],结果是 0。放进括号或赋值语句里,它才是真正的加法,结果是'[object Object]'。
面试官还会怎么问?
1 + - + + + - + 1 是多少?
除了第一个 +,后面都是一元运算符,从右往左作用在最后的 1 上。两个负号抵消,得到 1 + 1,结果是 2。
模板字符串和 + 拼接,转换规则一样吗?
基本一样,都是把值转成字符串。区别在于模板字符串对插值结果直接做字符串转换,而 + 走的是「先转原始值,提示为 'default'」。对定义了 Symbol.toPrimitive 的对象,两者可能得到不同结果。另外 Symbol 用 + 拼接和放进模板字符串都会抛 TypeError,要显式调用 String(sym)。
大量字符串拼接要不要改用数组 join?
以前的老 IE 里数组 join 更快,现在的引擎对 += 做了优化,性能差别很小。循环里拼 HTML 这类场景,选可读性好的写法就行,真遇到瓶颈再用性能面板测。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。