这是我参与更文挑战的第18天,活动详情查看: 更文挑战
前言
大家都知道,js是一个弱类型语言,所以它的隐式转换太神奇了,跟孙悟空的72变一样,变化多端,不管你是工作几年的老油条,还是初入职场的萌新,稍有不慎,可能你就踩坑了。这篇文章尝试把这个梳理一遍,争取能讲清楚,如果有问题,欢迎指正。
数据类型
我们先来了解js的数据类型有哪些?
基本类型: Number, Boolean, String,Null, Undefined, Symbol(新引入,这里暂不做讨论), BigInt(新引入,这里暂不做讨论)
引用类型: Object(Function,Array,Date等都属于Object)
隐式转换
转换规则
转换规则主要有这么几类
- 转原始值(ToPrimitive)
- 转字符串(ToString)
- 转数字 (ToNumber)
- 转布尔值 (ToBoolean)
下面一个个来讲
转原始值(ToPrimitive)
对象类型转原始值会遵循一下规则:
- 先调用对象的
valueOf方法,如果valueOf返回的是原始值,则返回该值; - 如果不是原始值,则再调用
toString方法,如果返回的是原始值,则返回该值; - 如果不是,则报错。
补充:
在valueOf方法和toString方法没有被改写的情况下
数组会返回字符串,逗号分隔,空数组会返回'',如果元素是null或者undefined,则会把该元素当作空字符串''
函数会返回该函数的字符串形式
对象会返回'[object Object]'
例子:
var obj = {
valueOf () {return '答案'},
toString () {return 'cp3' }
}
console.log(obj == '答案') // true
var obj = {
valueOf () {return {}},
toString () {return '答案cp3'}
}
console.log(obj == '答案cp3') // true
console.log([1,2,3] == '1,2,3') // true
console.log([] == '') // true
console.log(function a () {} == 'function a () {}') // true
console.log({} == '[object Object]') // true
转字符串(ToString)
别的类型转字符串的规则如下:
- Boolean类型:
true,false转成'true','false' - Number类型:
0,1转成'0','1' - Object类型: 遵循上面的对象转原始值流程;
- Null类型:
null转成'null' - Undefined类型:
undefined转成'undefined'
例子如下:
console.log(String(false)) // 'false'
console.log(String(123)) // '123'
console.log(String({})) // '[object Object]'
console.log(String(null)) // 'null'
console.log(String(undefined)) // 'undefined'
转数字 (ToNumber)
- Boolean类型:
true,false转成1,0 - String类型:数字字符串转成数字,空字符串转成
0, 其它字符串则是NaN - Object类型: 遵循上面的对象转原始值流程;
- Null类型:
null转成0 - Undefined类型:
undefined转成NaN
例子如下:
console.log(Number(false)) // 0
console.log(Number('123')) // 123
console.log(Number({})) // NaN 对象会先按照上面转原始值流程,转成字符串后再用字符串的方法,最后得到NaN
console.log(Number([1])) // 1
console.log(Number(null)) // 0
console.log(Number(undefined)) // NaN
转布尔值 (ToBoolean)
''0falsenullundefinedNaN这六个都是转布尔值是转成false,其它都是转成true
例子如下:
console.log(Boolean('')) // false
console.log(Boolean(0)) // false
console.log(Boolean(false)) // false
console.log(Boolean(null)) // false
console.log(Boolean(undefined)) // false
console.log(Boolean(NaN)) // false
console.log(Boolean('123')) // true
console.log(Boolean({})) // true
console.log(Boolean([1])) // true
规则主要是这些,下面来看看发生隐式转换的主要场景。
主要场景
发生隐式转换主要有==相等,, 算术运算符(+,-, *, /等), 比较运算符(>,<),if判断,while循环, 取非等场景,不同场景转换的类型不一样,运用的规则也不一样。
==相等
==和===不同,==不会判断类型,只会根据转换后的值去判断,如果相等,就相等。
有以下转换规则:
String类型和Number类型比较,String类型会转成Number类型(参考上面的String类型转Number规则),然后再比较。console.log('123' == 123) // true console.log('' == 0) // trueBoolean类型和其它类型比较,Boolean类型会转成Number类型(参考上面的Boolean类型转Number规则),然后再比较。console.log(1 == true) // true console.log(123 == true) // false console.log(0 == false) // true console.log('0' == false) // trueObject类型与其它类型比较- 如果是基本类型,则会转原始值(参考上面转原始值的规则)
- 如果是
Object类型,则会比较是不是同一个对象(栈地址相同),如果是同一个对象,则是true
console.log({} == '[object Object]') // true console.log({} == {}) // false 因为栈地址不一样 var a = b = {} console.log(a == b) // trueNull类型与Undefined类型相等,与其它类型都不相等。NaN与任何值都不相等。var a = null var b = undefined console.log(a == b) // true var a = b = NaN console.log(a == b) // false
算术运算符
- 减,乘,除(
-,*,/)会把类型转Number类型(依照上面转Number类型规则)。console.log('10' / 2) // 5 var a = { valueOf () {return 10} } console.log(a * 10) // 100 console.log(true - 1) // 0 - 加号(
+) 要分情况-
类型如果是
String类型,则会认为是拼接,而不是相加+ -
类型如果是
Object类型,如果拿到的原始值是String类型,则也是拼接。 -
其它类型,会转成
Number类型,再相加+ -
NaN与任何相加都会等于NaNconsole.log('1' + '2') // ‘12’ 拼接 var a = {valueOf () { return'2'}} console.log(1 + a) // '12' console.log(true + 2) // 3 console.log(undefined + 1) // NaN 因为undefined转成NaN console.log(null + 1) // 1 因为null转成0
-
比较运算符(>,<, >=, <=等)
其它类型跟数字比较,会把其它类型转成Number类型(参考各个类型转Number类型规则),然后再比较。
如果都是字符串类型,则不会转成
Number类型,而是用字符串的第一个字符的ASCII码比较
NaN与任何类型比较都会返回false
var a = {valueOf () { return 2}}
console.log(a > 1) // true
console.log('2' > 1) // true
console.log('a' > 1) // false
console.log(NaN > 1) // false
console.log('12' > '2') // false
if判断,while循环
会把类型转成Boolean类型(参考各个类型转Boolean类型规则),再判断。
if(123) // true
if(0) // false
while('答案cp3') // true
while(null) // false
取非(!)
会把类型转成Boolean类型(参考各个类型转Boolean类型规则), 然后再取反
console.log(!'') // true
console.log(!123) // false
console.log(![]) // false 因为先把[]转Boolean类型为true, 取反true为false,下同
console.log(!{}) // false
总结
讲了这么多,希望对你们理解js的隐式转换有一点点的帮助~