众所周知 javascript
是一种弱类型语言。强类型和弱类型主要是在变量类型处理的角度进行分类的。强类型是一旦指定数据类型,如果不经过强制转换,那么将永远是指定的这个类型。js
中无法声明数据类型,变量类型是根据实际值决定的,由编译器自动调用转换函数进行转换,这种方式称之为隐式转换,今天我们就来聊聊数据类型是如何隐式转换的吧!
可能很多小伙伴都知道 JS隐式转换
,但你是否真的懂它了呢?下面来试试吧!!
定义一个变量
a
,使得下面的表达式结果为true
a == 1 && a == 2 && a == 3
相信很多小伙伴都懵了,还能这样?别急,我们再来看看其他的!
[] == ![] // true
[] == 0 // true
[2] == 2 // true
['0'] == false // true
+[] // 0
+[1] // 1
+[1, 2] // NaN
这下好了,彻底懵了!!下面让我们来探讨一下呗~~
隐式类型转换是在一定场景下,js
运行环境自动调用这几个方法,尝试转换成期望的数据类型
这里所说的
ToString
可不是对象的toString
方法,而是指其他类型的值转换为字符串类型的操作
"null"
"undefined"
true
和 false
分别被转为 "true"
和 "false"
10
转为 "10"
, 1e21
转为 "1e+21"
Array.prototype.join()
方法,如 [1, 2, 3]
转为 "1,2,3"
,空数组 []
转为 ''
空字符串,数组中的 null
或 undefined
,会被当做 ''
空字符串处理Object.prototype.toString()
,返回 "[object Object]"
String(null) // 'null'
String(undefined) // 'undefined'
String(true) // 'true'
String(10) // '10'
String(1e21) // '1e+21'
String([1,2,3]) // '1,2,3' 相当于Array.prototype.join()
String([]) // ''
String([null]) // ''
String([1, undefined, 3]) // '1,,3'
String({}) // '[object Objecr]' 相当于Object.prototype.toString()
ToNumber
指其他类型转换为数字类型的操作
null: 转为 0
undefined:转为 NaN
字符串:如果是纯数字形式,则转为对应的数字,空字符转为 0
, 否则一律按转换失败处理,转为 NaN
布尔型:true
和 false
被转为 1
和 0
数组:数组首先会被转为原始类型,也就是 ToPrimitive
,然后在根据转换后的原始类型按照上面的规则处理,关于 ToPrimitive
,会在下文中讲到
对象:同数组的处理
Number(null) // 0
Number(undefined) // NaN
Number('10') // 10
Number('10a') // NaN
Number('') // 0
Number(true) // 1
Number(false) // 0
Number([]) // 0
Number(['1']) // 1
Number({}) // NaN
ToBoolean
指其他类型转换为布尔类型的操作
js
中的假值只有 false
、null
、undefined
、''空字符
、0
和 NaN
,其它值转为布尔型都为 true
Boolean(null) // false
Boolean(undefined) // false
Boolean('') // flase
Boolean(NaN) // flase
Boolean(0) // flase
Boolean([]) // true
Boolean({}) // true
Boolean(Infinity) // true
ToPrimitive
指对象类型(如:对象、数组)转换为原始类型的操作
valueOf
方法,如果 valueOf
方法返回原始类型的值,则 ToPrimitive
的结果就是这个值valueOf
不存在或者 valueOf
方法返回的不是原始类型的值,就会尝试调用对象的 toString
方法,也就是会遵循对象的 ToString
规则,然后使用 toString
的返回值作为 ToPrimitive
的结果注意:对于不同类型的对象来说,
ToPrimitive
的规则有所不同,比如Date
对象会先调用toString
,具体可以参考ECMA标准
如果 valueOf
和 toString
都没有返回原始类型的值,则会抛出异常
Number([]) // 0
Number(['10']) //10
Number(['10', '10']) // NaN
const obj1 = {
valueOf () {
return 100
},
toString () {
return 101
}
}
Number(obj1) // 100
const obj2 = {
toString () {
return 102
}
}
Number(obj2) // 102
const obj3 = {
toString () {
return {}
}
}
Number(obj3) // TypeError
前面说过,对象类型在 ToNumber
时会先 ToPrimitive
,再根据转换后的原始类型 ToNumber
Number([])
, 空数组会先调用 valueOf
,但返回的是数组本身,不是原始类型,所以会继续调用 toString
,得到空字符串,相当于 Number('')
,所以转换后的结果为 "0"
Number(['10'])
相当于 Number('10')
,得到结果 10
Number(['10', '10'])
,相当于 Number('10,10')
,得到结果 NaN
obj1
的 valueOf
方法返回原始类型 100
,所以 ToPrimitive
的结果为 100
obj2
没有 valueOf
,但存在 toString
,并且返回一个原始类型,所以 Number(obj2)
结果为 102
obj3
的 toString
方法返回的不是一个原始类型,无法 ToPrimitive
,所以会抛出错误下面只介绍常用的几种:
+
==
+
+1 // 1
+‘1’ // 1
+‘-1’ // -1
+{} // NaN
+[] // 0;
+[1] // 1;
+[1, 2] // NaN
二元加法运算符 +
可以对两个数字做加法,也可以做字符串连接操作,如果其中一个操作数是字符串或者隐式转换为字符串的对象,另外一个操作数将会转换为字符串,加法将进行字符串的连接操作
1 + 2 // 3: 加法
"1" + "2" // "12": 字符串连接
"1" + 2 // "12": 数字转换为字符串后进行字符串连接
1 + + '1' // 2 : 第二个+相当于数学中的正号
1 + {} // "1[object Object]": 对象转换为字符串后进行字符串连接
'1' + true // "1true": 布尔值转换为字符串后进行字符串连接
true + true // 2: 布尔值转换为数字后做加法
2 + null // 2: null转换为0后做加法
2 + undefined // NaN: undefined转换为NaN后做加法
1 + 2 + " blind mice"; // "3 blind mice"
1 +(2 + " blind mice"); // "12 blind mice"
//需要注意的是,“++”运算符从不进行字符串连接操作,它总是会将操作数转换为数字并增1。表达式++x并不总和x=x+1完全一样
let x = '1'
++x // 2:
==
规则:
ToNumber
操作后再进行比较ToNumber
操作后再进行比较ToPrimitive
操作后在进行比较true
,否则为 false
下面来看几个例子:
true == '1' // true
/**
* 布尔类型和其他类型比较适用规则2,true通过ToNumber操作转换为1
* 这时候1 == '1',这时候适用规则1,将'1'通过ToNumber操作转换为1
* 1 == 1 所以输出为true
**/
var obj = {
valueOf: function() { return '1' }
}
true == obj // true
/**
* 首先适用规则2,将true转换为1,此时1 == obj
* 此时适用规则3,将obj转换为'1',此时1 == '1'
* 此时适用规则1,将'1'转换为1,此时1 == 1,所以输出true
**/
[] == ![] // true
/**
* 一般直觉这明细是false,但我们仔细看一下
* ![]先对[]进行强制boolean转换,所以实际上应该是[] == false
* 这样就又回到我们刚刚的规则上了,适用规则2所以[] == 0
* 接着适用规则3,所以 '' == 0
* 最后 ToNumber('') == 0
**/
// 特例
NaN == NaN // false
null == undefined // true,属于ecma规范
我们看看文章开头说的那道题目的解法:
const a = {
// 定义一个属性来做累加
i: 1,
valueOf () {
return this.i++
}
}
a == 1 && a == 2 && a == 3 // true
// 或者
const a = {
// 定义一个属性来做累加
i: 1,
toString () {
return this.i++
}
}
a == 1 && a == 2 && a == 3 // true
这样就完美解决啦!!
你掌握了js数据类型隐式转换的方法了吗
参考文章:
https://juejin.cn/post/6844903694039777288