JavaScript中的类型转换

445 阅读5分钟

js中类型转换常出现在 判断语句、四则混合运算、并或

发生类型转换一般也是往这三个方向转换 转布尔值、转数字、转字符串

7大基础类型和1个引用类型

基础类型:Undefined、Null、Number、String、Boolean、Symbol、BigInt

引用类型:Object(函数,数组,Map,Set,Date,普通对象)

转布尔值

规则很简单,只有false0undefinednull、空字符串''这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

该方法定义了转换规则

  1. 如果是基础类型则直接返回
  2. 如果对象声明了Symbol.toPrimitive属性,则类型转换只会执行Symbol.toPrimitive方法,该方法接收一个type参数,需返回基础类型,否则抛出异常
  3. 若类型强转指定了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属性情况

  1. 若类型为Date类型,则先执行toString方法,默认会返回地区时间字符串。若重写导致未返回基础类型,则执行valueOf
  2. 其它类型,先判断是否是基础类型,如果是则直接返回,如果不是,则先执行valueOf,后执行toString

转数字的情况

  1. Number强转数字,即通过toPrimitive规则,传入type为number,返回基础类型,然后
  • null转换为0,undefined转换为NaN
  • false 转换为0,true转换为1
  • 字符串如果为纯数字格式'-1''+1''1.1',则可转,''空字符串可转为0,否则为NaN
  • Symbol类型抛出无法转换的异常
  1. 四则混合运算除了加号,均同Number
  2. 加号+:如果有一方通过toPrimitive(value, number)转换后为字符串,则加号两侧均转换为字符串。若没有字符串,则均转换为数字。加号若单独使用如+ '1',则会调用Number转数字
  3. 在大于小于号中,两侧均通过Number转数字或String转字符串,如有一方未转换成功,则返回false

parseIntparseFloat中,会在遇到第一个不是数字(非+-.)格式终止

转字符串情况

  1. String强转字符串,即通过toPrimitive规则,传入type为string,返回基础类型,然后参考toString规则进行转换
  2. 加号两侧如果有一方通过toPrimitive规则转换为字符串,则另一侧也需要通过String转换为字符串
  3. 在大于小于号中,两侧均通过Number转数字或String转字符串

双等号情况 ==

三等号情况是基础类型比较值,引用类型比较存储的内存地址。

双等号则会出现类型转换

  1. null、undefined不会发生类型转换,它们互等而不与其它任何相等
  2. 等号两侧类型相同,则基础类型比较值,引用类型比较存储的内存地址
  3. Boolean只能先转数字,false转换为0、true转换为1
  4. 其中有一侧toPrimitive后为数字,则另一侧也需转换为数字进行比较,否则为false
  5. 两侧toPrimitive后不为数字则需转换为字符串进行比较
  6. 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