我们需要了解任何计算都只能在相同的数据类型之间执行。如果我们强制JavaScript执行执行一些操作,例如在字符串中添加一个数字,在这种情况下,js编译器会默认将数字更改为字符串类型,然后将两者连接起来,这就是类型转换。

什么是类型转换?

在JavaScript中,类型转换是将一种数据类型转换为另一种数据类型,例如字符串转为数字,对象转为布尔值等。

使用不同的数据类型执行操作时,需要进行类型转换。这些转换可以由JavaScript编译器自动执行,也可以由我们手动执行。

我们看几个示例:

console.log('1' + 2) //'12'
console.log(1 + 'js')//1js
console.log(1 + 2)//3
console.log(1 + 2 + '1')//31
console.log(1 + '2' + 1)//121
console.log('6' - 2) // 4
console.log('6' * 2) // 12
console.log('6' / '2')//3
console.log(null == 1)//false
console.log('abc' - 1)//NaN

示例中使用不同的数据类型,默认情况下,js编译器对字符串类型数据使用 -、*、/ 运算符时会将字符串转换为数字;

而+运算符与其他运算符有所不同,+运算符执行两个功能:

1.数学加法

2.字符串串联

当对字符串使用+运算符操作时,js不是将字符串转为数字,而是将数字转换为字符串。

对于松散相等 == 运算符,如示例中,会把null转换为Number类型:0,热后比较0==1,返回false.

对于非数字型字符串使用-、*、/运算符,如示例中‘abc’-1,js编译器无法将‘abc’转换为数字类型,会返回NaN,表示不是一个数字。

类型转换的类型

在js中,有两种类型转换:隐式类型转换和显式类型转换。

  • 隐式类型转换:js编译器自动执行类型转换。
  • 显式类型转换:我们手动执行类型转换。

常见的数据转换:

  • 字符串的转换
  • 数字的转换
  • 布尔值转换

字符串转换

我们通常使用String()方法将其他数据类型转换为字符串类型,我们来看下面的示例。

//隐式转换
'15' + 36 // '1536'  36转为'36'
'20' + null //'25null'  null转为'null'
'abc ' + undefined //'abc undefined'  undefined转为'undefined'
'2' - 1 // 1   '2'转为2
6 / '2' //3  '2'转为2
'4' * 2 // 8   '4'转为4

//显式转换
String(23) // '23'
String(true) // 'true'
String({}) // '[object Object]'  调用目标对象toString()方法
String([]) // ''

在字符串和数字间使用-、*、/运算符,js编译器会将字符串转换为数字,如果是非数字型字符串,返回NaN;对于+运算符,会将数字转为字符串。

数字转换

我们可以使用Number()方法将其他类型数据显式转为数字类型。

//隐式转换
'' == 0 //true
[] == 0 // true  []=>''  '' == 0
true == 1 //true true=>1
false == 0// true false=>0

//显式转换
Number('12') // 12
Number('-12.34') // -12.34
Number('abc') // NaN
Number(null) // 0
Number(undefined) // NaN
Number({}) //NaN   调用目标对象valueOf()方法

非数字型字符串、undefined、对象在转换为数字类型时会转为NaN。

布尔值转换

我们可以使用Boolean()方法将其他数据类型转换为布尔类型。每一个js值都可以强制转换为true或false。在js中,以下值在转换为Boolean后返回false:

• false
• 0
• -0
• undefined
• ""
• NaN
• null

其他一切值都返回true。

Boolean(-1)//true
Boolean({})//true
Boolean([])//true
Boolean('0')//true

比较运算符、==、=== 

我们来看一下对于其他运算符的类型转换。

'2' > '1' // true  字符串之间比较第一个字符的Unicode编码 2.charCodeAt()>1.charCodeAt()
'2' > '10' // true  2的Unicode编码比1大  只比较第一个字符
'2' > 10 // false 字符串'2'转为数字2  然后进行比较
'abc' > 'aab' // true 先比较'a'和'a',两者相等,比较下一个字符,'b'>'a'
NaN == NaN // false NaN与任何数据比较都返回NaN
[] == 0 //true [].toString()=''  Number('') == 0成立
![] == 0 // true []转为布尔为true  取反得false false == 0 成立
[] == [] //false 引用数据类型存在堆中  栈存的地址引用  两个数组地址不同 返回false
[] == ![] //true
{} == {} //false 类型相同  直接比较  堆内存地址不同  返回false
{} == !{} //false // ????

我们发现了一个问题,为什么{} == {}返回false,{} == !{}也返回false,这是这么回事?

对于严格等于===运算符,会有严格得类型判断,对于对象和数组只看双方地址;对于松散等于==在比较时,如果双方类型相等,进行值或者地址比较,如果类型不相等,先进行类型转换,在比较值或者地址,所以[] == []和{} == {}返回false,因为堆内存中地址不同;我们还需要了解一下==运算符类型转换规则:

  1. 如果有一个操作数是布尔值,会将其转换为数字类型再进行比较,true:1,false:0;
  2. 如果一个操作数是字符串,另一个操作数是数值,会先将字符串转换为数值在进行比较,当左右两边类型相等时,采用===运算符断定规则。

回归正题,{} == !{}右侧为Object类型,左侧为Boolean类型,所以要将两侧操作数转换为Number类型,Number({})返回NaN,{}隐式转换为true,!{}等于!true =>false,Number(false)等于0,所以最终运算为NaN == 0,NaN与任何值比较都返回false,故结果为false。

额外补充

 有一个需要我们关注的点,看下面这个示例:

3 === new Number("3")  // false
3 === Number("3")  // true

两行代码都将String类型的‘3’转换成数字3,但返回结果不同;第一行代码返回false,因为我们创建了一个Number对象的实例,返回的是这个实例的引用;而第二行代码返回的是一个原始的数据类型,所以结果是true。

typeOf new Number("3") // object
typeOf Number("3") // number
typeOf 3 // number