引言
假设你在开发中写了这样一行代码:if ([]) { console.log('真') },它会输出什么?
又或者你看到这样的比较:[] == , String())
- 隐式转换:JS引擎在运行时自动进行的转换(如
==,+运算)
核心
理解JS如何在不同场景下自动转换类型
显式类型转换
转布尔值:Boolean()
Boolean('') // false
Boolean(0) // false
Boolean(null) // false
Boolean(undefined) // false
Boolean(NaN) // false
Boolean(false) // false
// 其他所有值都转为true
转数字:Number()
Number('123') // 123
Number('123abc') // NaN
Number('') // 0
Number(true) // 1
Number(false) // 0
Number(null) // 0
Number(undefined) // NaN
转字符串:String()
String(123) // '123'
String(true) // 'true'
String(null) // 'null'
String(undefined) // 'undefined'
String({}) // '[object Object]'
隐式类型转换
原始类型间的转换
'123' == 123 // true (字符串转数字)
1 + '2' // '12' (数字转字符串)
引用类型转原始类型
转布尔:所有对象都是true
Boolean([]) // true
Boolean({}) // true
转字符串:调用toString()
[1,2,3] + '' // '1,2,3'
({}) + '' // '[object Object]'
转数字:先调用valueOf(),再调用toString()
+[] // 0 ([] -> '' -> 0)
+[1] // 1 ([1] -> '1' -> 1)
+[1,2] // NaN ([1,2] -> '1,2' -> NaN)
类型转换的底层机制
ToPrimitive(obj, preferredType)
JS引擎转换引用类型的核心算法:
- 如果
preferredType是String:- 先调用
obj.toString() - 如果不是原始值,再调用
obj.valueOf()
- 先调用
- 如果
preferredType是Number:- 先调用
obj.valueOf() - 如果不是原始值,再调用
obj.toString()
- 先调用
valueOf()和toString()
// 默认valueOf()返回对象本身
({}).valueOf() // {}
// 数组重写了toString()
[1,2].toString() // '1,2'
// 对象默认toString()
({}).toString() // '[object Object]'
== 和 === 的区别
== (宽松相等)
会进行隐式类型转换
'123' == 123 // true
[] == false // true
null == undefined // true
=== (严格相等)
不进行类型转换
'123' === 123 // false
[] === false // false
null === undefined // false
发生隐式转换的常见场景
四则运算
1 + '2' // '12'
'3' * '4' // 12 (字符串转数字)
[1] + [2] // '12' (数组转字符串拼接)
判断语句
if ({}) { // 对象转布尔总是true
console.log('真')
}
比较运算
[] == 0 // true
[] == '' // true
[] == [] // false (引用类型比较地址)
+ 运算符的特殊性
一元运算符:转数字
+'123' // 123
+[] // 0
+{} // NaN
二元运算符:可能转字符串
1 + 2 // 3
1 + '2' // '12'
[] + {} // '[object Object]'
经典面试题解析
题目1:[] ==
- [] == false // true
- false转数字为0 // [] == 0
- []转字符串为'' // '' == 0
- ''转数字为0 // 0 == 0
- 结果:true
### 题目2:{} + []
```javascript
// 解析步骤:
1. {}被解析为代码块 // 相当于 +[]
2. +[] // 0 (数组转数字)
3. 结果:0
题目3:[] + {}
// 解析步骤:
1. []转字符串为'' // '' + {}
2. {}转字符串为'[object Object]'
3. 结果:'[object Object]'
面试官的鼻子怎么牵?
假设面试官问:JS中如何判断两个值是否相等?
可以这么回答: "在JS中,我们有==和===两种比较方式。==会进行隐式类型转换,比如'1' == 1是true;而===不会转换类型,要求类型和值都相等。"
"在比较对象时,==和===都是比较引用地址。如果想深度比较对象,可以递归比较每个属性,或者使用JSON.stringify()转换为字符串比较..."
"在类型转换方面,JS有一套复杂的转换规则。比如在+运算中,只要有一个操作数是字符串,就会进行字符串拼接;而其他算术运算符通常会把操作数转为数字..."
你都这么聊了,面试官当然要考考你:`[] == 后toString(),最终转数字 | | 字符串 | 对应字符串形式 | 调用toString() |
手写类型检查函数
// 判断是否为原始类型
function isPrimitive(val) {
return val === null ||
(typeof val !== 'object' && typeof val !== 'function')
}
// 模拟ToPrimitive转换
function toPrimitive(obj, preferredType) {
if (isPrimitive(obj)) return obj
preferredType = preferredType || 'default'
if (preferredType === 'string' || preferredType === 'default') {
const toString = obj.toString
if (typeof toString === 'function') {
const res = toString.call(obj)
if (isPrimitive(res)) return res
}
const valueOf = obj.valueOf
if (typeof valueOf === 'function') {
const res = valueOf.call(obj)
if (isPrimitive(res)) return res
}
}
else if (preferredType === 'number' || preferredType === 'default') {
const valueOf = obj.valueOf
if (typeof valueOf === 'function') {
const res = valueOf.call(obj)
if (isPrimitive(res)) return res
}
const toString = obj.toString
if (typeof toString === 'function') {
const res = toString.call(obj)
if (isPrimitive(res)) return res
}
}
throw new TypeError('Cannot convert object to primitive value')
}
// 测试
console.log(toPrimitive([])) // ''
console.log(toPrimitive([1,2], 'number')) // '1,2'
console.log(toPrimitive({}, 'string')) // '[object Object]'
现在,你已经掌握了JS类型转换的精髓,下次遇到这类问题时就能从容应对了!