面试之道——JS的类型转换

80 阅读7分钟

我们在写一些判断语句中,经常会使用=====符号来判断是否相等,但是这两个符号究竟有什么不一样呢?这就要了解JS的类型转换了,话不多说,直接涨知识! image.png

一、类型转换方式

JavaScript中实现类型转换通常有两种方式:显式类型转换隐式类型转换

1. 显式类型转换

JavaScript通过调用特定的方法或使用函数来显式转换类型,例如直接调用toString()、String()、Boolean()等函数。

2. 隐式类型转换

JavaScript在需要时会自动将引用类型转换为原始类型,这种转换就叫做隐式类型转换。通常发生的场景有:

  • 四则运算 + - * / %
  • 判断语句 if() while() == > < <= >= !=

所以,在JavaScript中,判断相等有两种情况:

  • == 会发生隐式类型转换,所以只判断值是否相等
  • === 不会发生隐式类型转换,意味着需要判断值和类型是否都相等,也叫全等于。

二、原始类型转原始类型

1. Boolean()  - 转换为布尔值

原始类型转换为布尔类型时:Number类型里面只有0和无效数字NaN会转换为false,另外空字符串转布尔类型也是false,其他都是true。不论显示转换还是隐式转换,我们都可以详细对照官方文档给的表格进行转换,如下: image.png 测试代码如下:

// 显示转换
console.log(Boolean(1)); // true
console.log(Boolean(true)); // true
console.log(Boolean(undefined)); // false
console.log(Boolean(null)); // false
console.log(Boolean(-1)); // true
console.log(Boolean(0)); // false
console.log(Boolean(NaN)); // false
console.log(Boolean('gagiuq')); // true
console.log(Boolean('')); // false

// 隐式转换
let arr = [1]
if(arr.length){ // 隐式类型转换成布尔类型 arr.length = 1 --> true
    console.log('hello'); // hello
}

2. Number() —— 转数字

Number类型包括:有效数字和无效数字NaN,而Number() 函数可以将值直接转换为数字,我们可以直接根据官方文档给出的表格进行判断: image.png

其中我们能够看到对于字符串转数字时,是有不同的结果:

  • 空字符串/空白符转数字,返回数字0
  • 纯数字字符串转数字,返回该数字
  • 非纯数字的字符串转数字:NaN
  • 带进制的数字字符串转数字:(0x、0X、0o、0O、0b、0B)转换成十进制数字

测试代码如下:

// 转数字类型
console.log(Number('1')); // 1
console.log(Number('0')); // 0
console.log(Number('')); // 0
console.log(Number(undefined)); // NaN
console.log(Number(null)); // 0
console.log(Number(true)); // 1
console.log(Number(false)); // 0
console.log(Number(1)); // 1 没有转换
console.log(Number('hello')); // NaN
console.log(Number('123hello')); // NaN

console.log(Number("0x77")); // 119
console.log(Number("0xda")); // 218   0x/0X是十六进制数
console.log(Number("0o77")); // 63    0o/0O是八进制数
console.log(Number('0b101')); // 5    0b/0B是二进制数

3. String() —— 转换为字符串

String() 可以将任意类型的值转化成字符串,原始类型直接就是将数值以字符串的形式返回。 image.png

测试代码如下:

// 显式转字符串
console.log(String(1)); // '1'
console.log(String(undefined)); // 'undefined'
console.log(String(null)); // 'null'
console.log(String(true)); // 'true'
console.log(String(false)); // 'false'
console.log(String(NaN)); // 'NaN'

三、引用类型转原始类型

1. 引用类型转布尔:true

官方文档说的:引用类型转布尔值就只会返回true。 image.png

2. 引用类型转字符串

引用类型转字符串可以使用String(obj) 或者 Object.prototype.toString(obj),原理是:

  • ToString({})
  • ToPrimitive({}, String)。 其实看到已经有点懵逼,我们先放一边,来看一段代码:
// 对象转换字符串
let a = {}
console.log(String(a));  // [object Object]
console.log(a.toString());  //[object Object]
console.log(a.__proto__ === Object.prototype); // true

首先我们看对象转换字符串的代码,可以看出String(a) 和 a.toString()返回的结果都是一样的,而且这个字符串有点眼熟!

没错在前面一篇文章中(JavaScript中的类型判断方法你知道几种?- 掘金),我们已经提到了Object.prototype.toString()方法去判断数据类型,返回的字符串就是[object Object]这种状态。这里调用的toString()是打造在Object的原型上的,a是可以访问到的。

我们再看第二段代码:

// 数组转换成字符串
let b = []  
console.log(b.toString());  // ''
console.log(Array.prototype == b.__proto__);  // true
console.log(Object.prototype == Array.prototype.__proto__);  // true

let arr = [1, 2, 3]
console.log(arr.toString()); // '1,2,3'

我们定义一个数组,从4、5行代码的原型链查找结果我们就可以看出,数组也是一个对象,所以它也是可以访问toString()方法的。理论上,如果b.toString访问的是Object.prototype.toString的话,应该是得到[object Array],但是结果却是空字符串,并且在第8行代码中返回的是一个数组元素以逗号拼接的字符串,所以应该是数组自身的隐式原型上存在一个toString()方法,如图:

image.png

同理,函数也是一个对象,如果函数自身的隐式原型上也存在一个toString方法,在函数调用它时也不会返回[object Funtion]字符串,我们看测试代码:

// 函数转字符串
let fn = function(){}
console.log(fn.toString()); // function(){}

代码结果已经显而易见了:函数自身的隐式原型上也存在一个toString方法。另外,我们还有其他引用类型,例如date,结果如图:

image.png 我们会发现date类型的toString()返回的结果状态和function类型是一样的,其实剩下的引用类型转字符串的状态也和这个相同。

由此可得,引用类型转字符串就是三种结果

  • {}.toString 会返回由 '[object' 和 class 和 ']' 组成的字符(返回对象的类型)
  • [].toString 返回由数组中元素以逗号拼接的字符串
  • xx.toString 直接返回 xx 的字符串字面量

3. 引用类型转数字 —— Number(obj)

原理:

  • ToNumber({})
  • ToPrimitive({}, Number)
  • 使用完ToPrimitive({}, Number)会转换成原始类型,然后就是开始原始类型转number的操作

我们看第一步,官方解释ToNumber({})传入一个对象会做什么?如图: image.png 根据文档提到的,会让这个对象作为一个参数,即V8会调用ToPrimitive({}, Number)方法,而在调用这个方法时内部会将对象转成字符串然后输出,然后再进行字符串转number类型。

image.png 如图就是将数组转成了字符串'1,2,3',然后再转数字就是NaN。

4. ToPrimitive

我们发现,在引用类型转字符串或者数字时,都调用了ToPrimitive(),最后返回相应的结果。 image.png ToPrimitive接收两个参数,一个是需要转换类型的对象,第二个是String或者Number,即想转换的类型。具体执行步骤如下:

image.png 所以,ToPrimitive(obj, String)ToPrimitive(obj, Number)只是内部的valueOf()toString()在执行顺序上面不一样。上面的翻译有点乱,我的总结如下:

(1) ToPrimitive(obj, String)

  • 如果obj是原始类型,直接返回;
  • 否则,调用toString(),如果得到原始类型则返回;
  • 否则,valueOf(),如果得到原始类型则返回;
  • 否则,报错。

(2)ToPrimitive(obj, Number)

  • 如果obj是原始类型,直接返回;
  • 否则,valueOf(),如果得到原始类型则返回;
  • 否则,调用toString(),如果得到原始类型则返回;
  • 否则,报错。

四、一元、二元运算符

1. 一元运算符 +

在 JavaScript 中,一元运算符(Unary Operators)是指仅作用于一个操作数的运算符,它的作用是把该操作数转换成number类型进行计算。(ToNumber())

例如:

console.log( +'123' ); // 123

2. 二元运算符(加法运算符)

在 JavaScript 中,二元运算符(Binary Operators)是指作用于两个操作数的运算符。常见的二元运算符包括算术运算符、比较运算符、逻辑运算符等。

我们这里以val1 + val2为例,官方文档给出的步骤如下:

(1)lprim = ToPrimitive(val1)

(2)rprim = ToPrimitive(val2)

  • 如果lprim 或者 rprim 是字符串,另一个值直接ToString(),即就是字符串的拼接,例如:123 + 'hello' = 123hello
  • 否则,返回对 ToNumber(lprim) 和 ToNumber(rprim) ,返回加法运算的结果。

OK,到这里就结束啦!!!终于写完了哈哈哈哈,喜欢的话,记得点个赞喔~谢谢!下次见

67d0405c9645alNb.gif