你不能不知道的:JS类型转换

469 阅读5分钟

在刚刚开始学习js时,有时会碰见这些情况: image.png 这些转换看着就让人头大,不禁感叹JS的类型转换也太灵活多变了,这都归结于JS作为一个动态弱类型语言的特性,它在好用的同时也带来很多麻烦。本文将详细地分析JS的类型转换到底是如何运行的。

首先我们知道js中基本类型有:undefined / null / number / string / boolean / bigint / symbol,基本类型数据放在栈内存中基本类型数据是不可变的。引用类型有:Object / Array / Function 等,本质上都是属于 Object,引用类型以:地址: 数据 的映射关系来进行存储,其中地址放在栈内存,数据放在堆内存,若两个或 N 个变量指向同一个地址,则共用一份数据。引用类型数据是可变的。原始数据类型为:number、string、boolean、null、undefined、object null

类型转换

1.显示类型转换

  1. 原始值转布尔
 Boolean()

  1. 原始值转数字
 Number()

 //ToNumber()是Number类型里的一个方法

 parseInt(3.14, 2) 
 // 将3.14看成2进制数,转出十进制
  1. 原始值转字符串
 String()

使用String()函数转换时,对Number和Boolean类型实际就是调用ToString()方法,但是对于null和undefined就会直接转换为'null','undefined'

  1. 原始值转对象
let a = 1
let b = new Number(a)//b是Number对象
let c = new String('2')//c时String对象
let d = new Boolean(false)//d是Boolean对象
QQ图片20220811191944.png image.png 在这里可以看见原始值转成的对象有一个[[PrimitiveValue]]原始值,而正常的obj对象是没有这个值的。
  1. 对象转布尔
Boolean(new Boolean(false))
  1. 对象转数字和字符串
 toString()  
 valueOf() //返回对象中的[[PrimitiveValue]]原始值
 
 Object.prototype.toString.call()  
//Object.prototype上的原生toString()方法判断数据类型
 
 Number(obj)
 String(obj)//将引用类型转换为基本类型
 

在String({})和 Number({})调用中,会执行

ToPrimitive(input, PreferredType)

它把对象转为原始类型,如果PreferredType不存在,且input是Date类型,相当于PreferredType == String

当调用Number(obj)时,执行ToPrimitive(obj, Number),执行过程为:

  1. 如果obj是基本类型,直接返回
  2. 否则,调用 valueOf 方法,如果得到一个原始类型,则返回
  3. 否则,调用 toString 方法,如果得到一个原始类型,则返回
  4. 否则报错

当调用String(obj)时,执行ToPrimitive(obj, String),执行过程为: ToPrimitive(obj, String)

  1. 如果obj是基本类型,直接返回
  2. 否则,调用 toString 方法,如果得到一个原始类型,则返回
  3. 否则,调用 valueOf 方法,如果得到一个原始类型,则返回
  4. 否则报错

在这里附上一张类型转换表:

原始值转换为数字转换为字符串转换为布尔值
false0'false'false
false0'false'false
true1'true'true
00'0'false
11'1'true
'0'0'0'true
'000'0'000'true
'1'1'1'true
NaNNaN'NaN'false
InfinityInfinity'Infinity'true
-Infinity-Infinity'-Infinity'true
''0''false
'20'20'20'true
'endless'NaN'endless'true
[ ]0''true
[20]20'20'true
[10,20]NaN'10,20'true
['endless']NaN'endless'true
['end','less']NaN'endless,endless'true
function(){}NaN'function(){}'true
function(){}NaN'function(){}'true
{ }NaN'[object Object]'true
null0'null'false
undefinedNaN'undefined'false

接下来我们来看看在隐示类型转换中,这些方法具体是如何调用的。

2.隐示类型转换

1)一元运算符

image.png


 +'1'  Number('1')

一元运算符 + 在这里相当于调用了Number(),其中调用了ToPrimitive(obj, Number),因为'1'是基本类型,所以直接返回,最后返回数字1。

 +[]  

同理,+[]相当于先调用了Number(),其中调用了ToPrimitive(obj, Number),在这里因为[]不是基本类型也没有原始值,所以调用toString方法得到空字符串'',最后返回数字0

2)二元运算符

在二元运算符运算时,遵循以下规则

v1 + v2

  1. lprim = ToPrimitive(v1)
  2. rprim = ToPrimitive(v2)
  3. 如果 lprim 是字符串或 rprim 是字符串,那么返回 ToString(lprim) 和 ToString(rprim)的拼接结果
  4. 否则返回 ToNumber(lprim) 和 ToNumber(rprim)的相加结果 image.png

举几个例子:

 1 + '1'
 //'1'+'1'
 //'11'
 //

在这里执行ToPrimitive(1)和ToPrimitive('1')

那么两边都转成了'1'

然后将两边拼接得到'11'

 [] + {}
 //'' + '[object, object]'
 //'[object, object]' 
 //
 

同理,先执行ToPrimitive([])和ToPrimitive({})

得到'' 和 '[object, object]'

然后将它们拼接得到结果'[object, object]'

不过在这里有一种特殊情况就是:

 {} + [] 
 //+[]
 //0

在识别的时候会将{}识别成代码块,所以在这里变成了+[],所以最后得出的结果为0。

3)==

x == y

遵从以下规律

1.如果 x 和 y 是同一类型,那么

  1. 如果x 是 Undefined,返回true

  2. 如果 x 是 null,返回true

  3. 如果 x 是数字,那么

    1. 如果 x 是 NaN ,返回 false
  4. 如果x和y指向同一个对象,返回true,否则返回false

2 如果x 和 y 不是同一类型,那么先将两边转为原始类型,然后再转换为数字

image.png 接下来上几个例子

1=='h'
//ToNumber('h')
//false

在这里两边都是原始类型,而且1是数字,所以执行ToNumber('h'),得到右边是0,最后输出结果为false ``

true=={a:1}
//  true =='[object Object]'
//  1==NaN
//  false
//
//
//

在这里会执行ToPrimitive({a: 1}),得到'[object Object]',然后执行ToNumber('[object Object]'),右边为NaN,所以输出false

[] == ![]
// []== false
// ''==0
//
//

在这里因为!的优先级大于==,所以先执行![],将其进行强制boolean转换为false。 左边转换为''之后再转换为0,所以最后得到0 == 0,输出true

image.png