"1 + '1' = ?" ------ 这个经典面试题背后,隐藏着 JavaScript 最微妙的类型转换规则
一、'+' 操作符的双重人格
JavaScript 的 +
是唯一具有双重功能的操作符:
-
数值相加 :当两边都是数字时
javascript2 + 3 = 5 // 纯粹的数学运算
-
字符串拼接 :当至少一边是字符串时
javascript'2' + 3 = '23' // 触发强制类型转换
二、隐式转换优先级规则
flowchart TD
A[操作数类型判断] --> B{是否有字符串?}
B -->|是| C[转换为字符串拼接]
B -->|否| D[转换为数字相加]
三、9 种经典场景分析
1. 数字 + 字符串
javascript
1 + '1' // '11'
NaN + 'a' // 'NaNa'
Infinity + '1' // 'Infinity1'
2. 布尔值参与运算
javascript
true + 1 // 2 (true→1)
false + '1' // 'false1'
3. 对象类型转换
javascript
[] + {} // '[object Object]'
{} + [] // 0 ({}被解析为空代码块)
4. Date 对象特例
javascript
new Date() + 1000
// "Wed Jul 19 2023 14:30:00 GMT+08001000"
5. null/undefined 处理
javascript
null + 1 // 1 (null→0)
undefined + 1 // NaN
6. 符号类型报错
javascript
Symbol(1) + 1
// TypeError: Cannot convert a Symbol value to a number
7. 数组的自动展开
javascript
[1] + [2] // '12'
[1,2] + [3] // '1,23'
[] + [] // ''
8. 函数返回值
javascript
function fn() {}
fn + 1 // 'function fn() {}1'
9. 连续转换陷阱
javascript
1 + 2 + '3' // '33'
1 + (2 + '3') // '123'
四、核心转换机制拆解
对象转换三部曲
-
调用
valueOf()
尝试获取原始值 -
如果未得到基本类型,调用
toString()
-
最终转换规则:
javascript[] → '' // Array {} → '[object Object]' // Object /regex/ → '/regex/' // RegExp
类型转换优先级表
类型 | 转字符串 | 转数字 |
---|---|---|
'' |
'' | 0 |
'0' |
'0' | 0 |
'10' |
'10' | 10 |
null |
'null' | 0 |
undefined |
'undefined' | NaN |
true |
'true' | 1 |
false |
'false' | 0 |
五、防御性编程技巧
1. 强制显式转换
javascript
// 统一转换为数字
+ '123' // 123
Number('123') // 123
// 统一转换为字符串
String(123) // '123'
123..toString() // '123'
2. 模板字符串优先
javascript
`${1}${2}` // '12' 替代 1 + ''
3. 使用 Map/Set 替代对象
javascript
const map = new Map()
map.set(1, 'one')
1 + map.get(1) // '1one' 但逻辑更清晰
4. 严格模式检测
javascript
'use strict';
const a = 1
a + () // 更好的错误提示
六、总结:掌握转换规律
记住三个黄金法则:
- 字符串优先:只要有一个操作数是字符串,执行拼接
- 对象转基本 :通过
valueOf()
/toString()
转换 - 布尔转数字 :
true→1
/false→0
通过理解这些规则,可以:
- ✅ 避免
0 == ''
之类的诡异判断 - ✅ 写出类型安全的数学运算
- ✅ 快速定位隐蔽的类型转换 Bug
记住:隐式转换不是魔法,而是遵循明确规则的机制!