【面试题】:js类型转换,你知道多少?

381 阅读8分钟

1.前言

谈到类型转换, 那么我们先简要列一下js常见的数据类型: 七大原始数据类型为string, number, boolean, undefined, null, symbol, bigInt和引用数据类型Object

js的类型转换只有三种类型的转换: to string, to boolean, to number, 即原始数据类型{string, number, boolean, undefined, null} + 引用数据类型{object, ... } to→ {string, boolean, number}的类型转换.

而在这三种类型转换当中, 分为两大块: 显式类型转换隐式类型转换. 显式类型转换是隐式类型转换的基础, 到后面你会发现, 隐式类型转换就是在操作符的作用下进行显式类型转换, 所以着重掌握显式类型转换的机制, 然后再记住哪些操作符会触发隐式类型转换即可。

2.定义+代码解析

1.显式类型转换

在了解隐式转换的规则前先来看看强制类型转换,强制类型转换主要是用Boolean()/String()/Number()将各类型的数据转换成布尔、字符串、数值型数据。

1.Boolean()函数

Boolean()函数默认是false,还有这几种情况:undefined,null,+0,-0,NaN,''(空字符串),其它全为true。注意Boolean([])=true,空数组转化为布尔值也是true,因为数组也是对象,凡是对象转化为Boolean都为true

//全部输出false
console.log(Boolean()) // false 默认
console.log(Boolean(0))
console.log(Boolean(undefined))
console.log(Boolean(null))
console.log(Boolean(+0))
console.log(Boolean(-0))
console.log(Boolean(NaN))
console.log(Boolean(''))

//下面全为true
console.log(Boolean(1)) //除0以外的数字
console.log(Boolean({}))
console.log(Boolean([]))

2.Number()函数

  • null、''(空字符串)、[] ->0;
  • fasle->0、true->1
  • "number"->number (连续的数字字符串,去掉前面的0)
  • "<16进制数>" -> <16 进制数> -> 10 十进制数
  • 其它都为NaN
console.log(Number()); // 0 默认
console.log(Number(null)) //0
console.log(Number(false)) // 0
console.log(Number(true)) // 1
console.log(Number('123')) // 123
console.log(Number('-123')) // -123
console.log(Number('1.23')) //1.23
console.log(Number('000123')) //123 
console.log(Number('-000123')) //-123 (去掉前面的0,保留了符号)
console.log(Number('0x17')) // 23 (默认转化为十进制)
console.log(Number('')) //0
console.log(Number(' ')) // 0
console.log(Number([])) // 0

console.log(Number(undefined)) // NaN
console.log(Number('111 222')); // NaN
console.log(Number('aaa')); // NaN
console.log(Number({})); //NaN
console.log(Number(NaN));//NaN

3.String()函数

最后一种强制类型转换方法 String() 是最简单的,因为它可把任何值转换成字符串。

console.log(String(undefined)) //undefined (字符串)
console.log(String([])) // ''(空字符串)
console.log(String({})) // [object Object] (对象的字符串形式)
console.log(String('')) // ''(空字符串)
console.log(String(null)) //null
console.log(String(true)) //true
console.log(String(123)) //123
console.log(String('123')) 123

4.对象 to {string, number, boolean}的 To Primitive类型转换

js的类型转换只有其他类型到{string, number, boolean}的类型转换, 其中对象到{string, number, boolean}的类型转换称作为to primitive类型转换, 即对象转原始数据类型

1.to string:

  • 调用对象的toString()方法, 没有则去原型链上查找
    • 如果toString返回值为原始值, 对返回值进行原始值 to string的类型转换 转换后的结果即为对象 to string的类型转换结果
    • 如果toString返回值为对象, 那么将调用<obj>valueOf()方法
    • 如果返回值为原始值, 对返回值进行原始值to string的类型转换, 转换后的结果即为对象 to string的类型转换结果
    • 如果返回值为对象, 则报错 3. to number:
  • 调用对象的valueOf()方法, 没有则去原型链上查找
    • 如果valueOf返回值为原始值, 对返回值进行原始值 to number的类型转换(2.2 节), 转换后的结果即为对象 to number的类型转换结果
    • 如果valueOf返回值为对象, 那么将调用<obj>toString ()方法
    • 如果返回值为原始值, 对返回值进行原始值to number的类型转换, 转换后的结果即为对象 to number的类型转换结果
    • 如果返回值为对象, 则报错

2. to boolean: 关于对象转为布尔值的机制很简单, 一般情况下, 对象to boolean都是直接转换为true, 而且不会调用对象的[Symbol.toPrimitive], toString, valueOf 这三个方法.

2.隐式类型转换

隐式类型类型转换大多发生在操作符当中, 比如宽松相等操作符==两端的类型不同的时候, 会发生隐式类型转换。 而有时候, 某些函数的参数如果是对象, 也会触发对象的类型转换。

1.一元操作符

+val | -val 会默认调用 ToNumber 处理val。 如果val是对象类型,会先调用ToPrimitive()方法,执行的步骤是:

  1. 如果 val 是基本类型,就直接返回
  2. 否则,调用 valueOf 方法,如果返回一个原始值,则js将其值返回
  3. 否则,调用 toString 方法,如果返回一个原始值,则js将其值返回
  4. 否则,报类型错误 举个例子:+['1']=1怎么来的:首先['1']判断为对象,调用valueOf()方法(['1'].valueOf()=['1']),还是对象,然后再调用toString()方法(['1'].toString()='1'),结果为字符串,将其返回,+'1'会自动转化为1
 console.log(+'1') // 1 ->Number('1')
 console.log(+[]) //0 ->Number([]) 
 console.log(+['1']) //1
 console.log(+['1','2','3']) // NaN
 console.log(+{}) //NaN
 console.log(-'123') //-123
 console.log(+'aaa') //NaN

2.二元运算符

如:val1 + val2

  1. v1 = Toprimitive(val1)
  2. v2 = Toprimitive(val2)
  3. 如果v1和v2其中一个为字符串,那么返回ToString(v1)和ToString(v2)的拼接结果,否则转4
  4. 返回 ToNumber(v1) 和 ToNumber(v2) 的运算结果 举个例子:{}+[]='[object Object]'结果是个对象的字符串形式,如何转化的呢:首先Toprimitive({}):->{}->'[object Object]'; Toprimitive([]):->[]->''; '[object Object]'+ '' ='[object Object]'

特殊情况: new Date()(也是对象) 会跳过步骤2,直接调用ToString()方法。本来new Date().valueOf会得到时间戳,是number类型,结果不会是字符串。说明跳过了valueOf()方法,比较特殊。

 console.log([]+[]) // ''
 console.log([]+0) // '0'
 console.log({} + []) // '[object Object]'
 
 console.log(new Date(2021,11,20) + 1) //'Mon Dec 20 2021 00:00:00 GMT+0800 (中国标准时间)1'

3.宽松相等运算符的隐式类型转换

两等号A == B的隐式类型转换规则其实不是很难, 个人总结的规则如下:

1.不会发生隐式类型转换的情形:(特殊情况)

  • A和B均为{undefined, null}当中的一种, A == B -> true
  • A为{undefined, null}当中的一种, B为{string, boolean, number, 对象}当中的一种, A == B -> false
  • A和B对象均为对象, 则比较对象的地址是否相等 2.会发生隐式类型转换的情形, 以及类型转换规则:
  • A 和 B均为{string, boolean, number}当中的一种, 且 A 和 B 的类型不一致. 那么,string和boolean都会先转为number, 然后再进行比较
  • A 和 B 其中有一个为对象, 另一个为{string, boolean, number}中的一种. 此时一个为对象, 一个为原始值, 当两者进行相等比较的时候, 会对对象进行 to primitive 的类型转换 注意, 在隐式类型转换当中, 如果对象发生to primitive操作总是优先调用[Symbol.toPrimitive]. 如果没有定义则调用valueOf, 在valueOf返回对象的时候, 会继续调用toString方法。
  • [Symbol.toPrimitive], toString, valueOf这三个方法在返回原始值的时候, 不会将原始值再转为其他的原始值类型, 而是直接返回结果, 作为对象的to primitive 类型转换结果 示例: [] == ![]的结果是什么?(面试常问题,重点笔记)

解答: 结果为true

  1. 从左到右进行扫描,因为!优先级大于==,所以首先![]触发类型转换, 将 []转为boolean,结果为true,取反为false。
  2. 然后判断[] == false, 此时两端类型不一致, 因而触发数组对象[]的to primitive隐式类型转换, 调用[].prototype.valueOf()返回数组对象自身, 因而继续调用[].prototype.toString()返回''
  3. 然后判断'' == false, 将''false都转换为number, 再进行比较, 最终为0 == 0, 结果为true

3.小结

1.显式类型转换

  • to Boolean :除了这些undefined,null,0,+0,-0,NaN,''(空字符串)为false,其它全为true
  • to Number :
    • null、''(空字符串)、[] ->0;
    • fasle->0、true->1
    • "number"->number (连续的数字字符串,去掉前面的0)
    • "<16进制数>" -> <16 进制数> -> 10 十进制数
    • 其它都为NaN
  • to String :可把任何值转换成字符串
  • 对象 to {string, number, boolean}的 To Primitive类型转换:
    • to boolean 全为true
    • to string :先调用toString()方法,是原始值返回toString结果,否则调用valueOf()方法,是原始值返回toString结果,否则报错。
    • to number: 先调用valueOf()方法,是原始值返回toNumber结果,否则调用toString()方法,是原始值返回to number结果,否则报错。

2.隐式类型转换

一元操作符会默认调用 ToNumber 处理该值、二元运算符会对对象分别调用ToPrimitive()方法、宽松运算符则是对有对象那一项进行调用ToPrimitive()方法。 ToPrimitive()方法的执行的步骤是:

  • 如果 val 是基本类型,就直接返回
  • 否则,调用 valueOf 方法,如果返回一个原始值,则js将其值返回
  • 否则,调用 toString 方法,如果返回一个原始值,则js将其值返回
  • 否则,报类型错误 所以只需要记住一些强制类型转换的特例,然后再记住ToPrimitive()方法的规则,就可以理清楚一些复杂的式子:[] == [] ->false、[]==![] ->true、if([])->true。你学废了吗?