js类型转换
Javascript
隐式类型转换
类型都有什么
- Number
- String
- Boolean
- Null
- Undefined
- Object
- Symbol (ES2015)
- BigInt (ESNext stage 4)
是不是感觉还有Function,毕竟能用typeof获取到?不,函数、数组都是Object的子类型。
类型分为基本类型
和复合类型
两种,除了对象,其它都是基本类型
接下来有请重量级嘉宾
To Primitive
结构:toPrimitive(input: any, preferedType?: ‘string’ |‘number’)
作用:内部方法,将任意值转换成原始值
转换规则:
- 如果是基本类型,则不处理。
- 调用
valueOf()
,并确保返回值是基本类型。 - 如果没有valueOf这个方法或者valueOf返回的类型不是基本类型,那么对象会继续调用
toString()
方法。 - 如果同时没有valueOf和toString方法,或者返回的都不是基本类型,那么直接抛出
TypeError
异常。
注意:如果preferedType=string,那么2、3顺序调换
接着,我们看下各个对象的转换实现
对象 | valueOf() | toString() | 默认 preferedType |
---|---|---|---|
Object | 原值 | “[object Object]” | Number |
Function | 原值 | “function xyz() {…}” | Number |
Array | 原值 | “x,y,z” | Number |
Date | 数字 | “Sat May 22 2021…” | String |
- 数组的toString()可以等效为
join(",")
,遇到null, undefined都被忽略,遇到symbol直接报错,遇到无法ToPrimitive的对象也报错。 - 使用
模板字符串
或者使用String(...)
包装时,preferedType=string,即优先调用 .toString()。 - 使用
减法
或者Number(...)
包装时,preferedType=number,即优先调用.valueOf()
接下来看几个🌰
1 |
|
ToNumber
一些特殊值转为数字的例子,等下要用到
1 |
|
加减法 ±
加减法运算中遵循了一些隐式转换规则:
遇到对象先执行ToPrimitive转换为基本类型
- 加法(+)运算,preferedType是默认值
- 减法(-)运算,preferedType是Number
1 |
|
非字符串 + 非字符串,两边都会先ToNumber
这里的非字符串都是指基本类型,因为对象会先执行ToPrimitive变成基础类型
1 |
|
1 + undefined + [1] === “NaN1”
• 1 + undefined 部分:
• 如前所述,1 + undefined 的结果是 NaN。
• NaN + [1]:
• [1] 是一个数组对象,当数组对象与非字符串类型相加时,JavaScript会调用它的 toString() 方法,将数组转换为字符串。
• [1].toString() 返回 “1”。
• 因此,NaN + [1] 等同于 NaN + “1”。
• NaN 被隐式转换为字符串 “NaN”,然后与 “1” 进行字符串连接。
任意值 - 任意值,一律执行ToNumber,进行数字运算。
此时的 preferedType === number
1 |
|
再看[] + {}
,这样是不是就easy
1 |
|
{} 在最前面时可能不再是对象
不是对象是什么?别急,看看经典的例子
1 |
|
这啥玩意?说好的"[object Object]"呢?
好吧,这是{}
其实代表的是代码块,最后就变成了+ []
,根据前面的原则,数组先被转换成字符串""
,接着因为+x的运算,字符串被转成数字0
。
那 { a: 2 } 总该是对象了吧?其实这时候a
不是代表对象属性,而是被当成了标签(label),标签这东西IE6就已经有了。所以如果我们写成对象是会报错的,逗号要改成分号才能通过编译。
1 |
|
symbol不能加减
如果在表达式中有symbol类型,那么就会直接报错。比如1 + Symbol("x")
报错如下:
1 |
|
宽松相等 ==
相等于全等都需要对类型进行判断,当类型不一致时,宽松相等会触发隐式转换。下面介绍规则:
对象 == 对象,类型一致则不做转换
1 |
|
对象 == 基本值,对象先执行ToPrimitive转换为基本类型
1 |
|
布尔值 == 非布尔值,布尔值先转换成数字,再按数字规则操作
1 |
|
数字 == 字符串,字符串ToNumber转换成数字
1 |
|
null、undefined、symbol
null、undefined与任何非自身的值对比结果都是false,但是null == undefined
是一个特例。
1 |
|
对比 < >
对比不像相等,可以严格相等(===)防止类型转换,对比一定会存在隐式类型转换。
对象总是先执行ToPrimitive为基本类型
1 |
|
后面这两个还挺有说法的
{} < {}
• 代码块 vs 表达式:
• 在没有括号的情况下,{} 被解释为一个代码块而不是对象字面量。
• 因此,{} < {} 被解释为一个空代码块 < 一个对象字面量的比较。
• 由于第一个 {} 被当作代码块,< 运算符之后的 {} 实际上是一个语法错误,因为它看起来像一个对象字面量的定义,但没有有效的上下文。
实际上,{} < {} 在大多数情况下会导致语法错误,但在某些环境中,可能解释为 false,因为 JavaScript 不能比较两个代码块。
加上括号就是
({} < {}):{} 被解释为对象字面量,并且 () 括起来确保 {} < {} 作为对象字面量被正确解析。
在这种情况下,{} 转换为 “[object Object]”,而 “[object Object]” < “[object Object]” 为 false,因为任何两个相同的字符串比较都是 false。
{} <= {}
• 代码块 vs 表达式:
• 和上面一样,{} <= {} 被解释为一个代码块 < 一个对象字面量的比较。
• 由于 <= 运算符和 < 运算符一样,可能导致语法错误或无效的比较。
实际上,{} <= {} 也可能会导致语法错误或被解释为 true 在某些特定的解析环境中,虽然理论上它是不被允许的。
任何一边出现非字符串的值,则一律转换成数字做对比
1 |
|
To Boolean
既然是总结,那么可能还要讲一下布尔值的隐式转换。这个还是比较常见的,我们来看下有哪些地方会使用到:
- if(…)
- for(;…😉
- while(…)
- do while(…)
- … ? :
- ||
- &&
既然知道会转换,那么什么值是真值,什么值是假值呢?换个思路,假值以外都是真值。看看哪些是假值:
- undefined
- null
- false
- +0
- -0
- NaN
- “”
总结
对象都需要先ToPrimitive转成基本类型,除非是宽松相等(==)时两个对象做对比。
- + 没有字符串就全转数字
- - 全转数字,preferedType===Number
- == 同类型不转,数字优先,布尔全转数字,null、undefined、symbol不转
- <> 数字优先,除非两边都是字符串