js中类型转换常出现在 判断语句、四则混合运算、并或
发生类型转换一般也是往这三个方向转换 转布尔值、转数字、转字符串
7大基础类型和1个引用类型
基础类型:Undefined、Null、Number、String、Boolean、Symbol、BigInt
引用类型:Object(函数,数组,Map,Set,Date,普通对象)
转布尔值
规则很简单,只有false、0、undefined、null、空字符串''这5种才能转false,document.all由于历史原因也会返回false,其余情况均为true。
判断语句有: if判断、while判断、三目运算符判断、!判断。Boolean强转也会根据上面规则进行转换。
const obj = {
valueOf: () => false,
toString: () => false,
[Symbol.toPrimitive]: () => false
}
if (obj) {console.log(true)}
valueOf
- 数组、Object类型,均返回自身
- Date类型返回时间戳,是Number类型
- function返回定义的代码
- null、undefined均抛出异常
- Symbol可以执行
toString
- Object类型默认返回
[object, Object],第二个Object为类型的类名Object.prototype.toString.call([]) === '[object Array]',同时可以重新声明Symbol.toStringTag属性改变({[Symbol.toStringTag]: 'A'}).toString() === '[object A]'(Map,Set的原型上都有该属性) - Date类型返回地区时间字符串
- function返回定义的代码字符串
- null、undefined均抛出异常,但可以在转换时转换为
'null'、'undefined' - 数组类型会每项调用toString方法,然后调用
join(',')转字符串 - Symbol可以执行,但在强转数字时会抛出异常
- 其它均会返回相应字符串,这里就不一一描述
toPrimitive(value: any, type?: string | number)
在介绍转数字和转字符串之前,先介绍toPrimitive
该方法定义了转换规则
- 如果是基础类型则直接返回
- 如果对象声明了Symbol.toPrimitive属性,则类型转换只会执行Symbol.toPrimitive方法,该方法接收一个type参数,需返回基础类型,否则抛出异常
- 若类型强转指定了toPrimitive的type,则type为string执行先toString,type为number先执行valueOf,执行其中一个未返回基础类型,则会调用另外一个方法,若还未返回基础类型,则抛出异常。(其中先执行一个后执行另一个,未有基础类型抛出异常在下面的规则均适用)
const obj = {
valueOf: () => '123',
toString: () => 'Hello',
}
console.log(String(obj)) // Hello
console.log(Number(obj)) // 123
以下均为未声明Symbol.toPrimitive属性情况
- 若类型为Date类型,则先执行toString方法,默认会返回地区时间字符串。若重写导致未返回基础类型,则执行valueOf
- 其它类型,先判断是否是基础类型,如果是则直接返回,如果不是,则先执行valueOf,后执行toString
转数字的情况
- Number强转数字,即通过toPrimitive规则,传入type为number,返回基础类型,然后
- null转换为0,undefined转换为NaN
- false 转换为0,true转换为1
- 字符串如果为纯数字格式
'-1'、'+1'、'1.1',则可转,''空字符串可转为0,否则为NaN - Symbol类型抛出无法转换的异常
- 四则混合运算除了加号,均同Number
- 加号
+:如果有一方通过toPrimitive(value, number)转换后为字符串,则加号两侧均转换为字符串。若没有字符串,则均转换为数字。加号若单独使用如+ '1',则会调用Number转数字 - 在大于小于号中,两侧均通过Number转数字或String转字符串,如有一方未转换成功,则返回false
parseInt、parseFloat中,会在遇到第一个不是数字(非+-.)格式终止
转字符串情况
- String强转字符串,即通过toPrimitive规则,传入type为string,返回基础类型,然后参考toString规则进行转换
- 加号两侧如果有一方通过toPrimitive规则转换为字符串,则另一侧也需要通过String转换为字符串
- 在大于小于号中,两侧均通过Number转数字或String转字符串
双等号情况 ==
三等号情况是基础类型比较值,引用类型比较存储的内存地址。
双等号则会出现类型转换
- null、undefined不会发生类型转换,它们互等而不与其它任何相等
- 等号两侧类型相同,则基础类型比较值,引用类型比较存储的内存地址
- Boolean只能先转数字,false转换为0、true转换为1
- 其中有一侧toPrimitive后为数字,则另一侧也需转换为数字进行比较,否则为false
- 两侧toPrimitive后不为数字则需转换为字符串进行比较
- NaN和NaN永远不相等
例子
特别注意{}在运算中若在表达式开头,则会被js引擎(比如V8)解析成语句块
const fn = () => {} // 最后没有返回值
const fn = () => ({}) // 最后返回空对象
{} + 1 // 1
{} + [] // 0
{a: 1} + 1 // 抛出异常,因为在块中 a: 1 是非法表达式
1 + {} // '1[object, Object]'
解释一下{} + []: toPrimitive流程(省略了一小部分):{}解析成块,省略,相当于 + [], []转换成数字会调用valueOf得到[]不是基础类型,再调用toString返回空字符串'',然后转数字得到0
但是在某些特殊情况{}由不会被解析成块,比如{} + {}、{} + function a() {}等,在老的浏览器或许是解析成块,现代浏览器都逐渐修复统一成一致。 笔者也还未弄明白,有知道的大佬麻烦留言指导一下。
所以,我们编程时,若想得到对象,请加个小括号避免可能存在的问题
例一
console.log({} + []) 由于作为参数传递,{}没有解析成块
例二
[] + 1 由于[]通过toPrimitive得到的是字符串,那么得到的是字符串'1'
例三
[] == ![] 右侧为布尔值,左侧为空字符串则布尔值先转换成数字0,0和空字符串相等
例四
'0' == 0 == '' 前面为true,true和空字符串均转数字1 !== 0,故结果为false