JS中的类型转换

122 阅读7分钟

我们先来看一个很经典的面试题:[] == ![],这个判断返回的是true还是false呢?大家可能第一时间都无法得出答案,而这个问题就涉及到了JS中的类型转换机制,今天我们就来带着上面提到的这个问题一起探讨一下JS的类型转换机制。

== VS ===

在我们日常判断同类型的数据是否相等时,用 == 或者 === 起到的效果都一样。如下图:

image.png

但当两边的数据类型不一样时,这两个判断方式就有差别了。让我们看个例子:

image.png

那这又是为什么呢?这就是JS中=====的区别。当等号两边的数据类型不同进行判断时,==会发生隐式类型转换,所以只会判断等号两边的值是否相等即可;而===不会发生隐式类型转换,因此需要判断等号两边数据的数据类型和值是否都相等

原始类型之间的转换

bigint和symbol是js后来才增加的原始类型,他们不会发生类型转换,因此我们在这里不考虑他们

1.转布尔

这个比较简单,我们直接看例子

console.log(Boolean(true));  
console.log(Boolean(false));  
console.log(Boolean(undefined));  
console.log(Boolean(null));  
console.log(Boolean(NaN));  
console.log(Boolean('0'))  
console.log(Boolean('hello'))  
console.log(Boolean(''))  
console.log(Boolean(1));  
console.log(Boolean(-1));  
console.log(Boolean(0));  

image.png

原始类型转布尔:

null和undefined返回false

对于数字类型:0和NaN返回false,其他的都返回true

对于字符串类型:空字符串返回false,其他都返回true

2.转数字

同样,我们直接用例子分析:

console.log(Number(true));
console.log(Number(false));
console.log(Number('1'));  
console.log(Number(''));  
console.log(Number('0'));  
console.log(Number());  
console.log(Number(null));  
console.log(Number(undefined));  
console.log(Number('hello'));  

image.png

原始类型转数字:

对于布尔类型:true返回1,false返回2

对于null:返回0

对于undefined:返回NaN

对于字符串类型:如果该字符串中只有数字类型,那就返回这个数字,如果包含了除数字以外的东西,比如英文字母,标点符号等,都返回NaN

3.转字符串

原始类型转字符串就比较的简单粗暴了,直接将传入的参数用引号括起来返回字符串即可,大家这里可以自行敲一些代码进行尝试

对象转原始值

这里我们直接对着js官方文档进行分析

image.png

image.png

image.png

image.png

1.转字符串

总结文档我们可以得到:在v8调用ToString()函数发现传入的参数是一个对象是,这个函数自己解决不了,会再交给ToPrimitive(obj, String)函数进行处理,它可以大致总结为以下三种情况:

ToPrimitive(obj, String)

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

toString()函数的调用也会有三种情况,如下:

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

让我们用例子看一下:

let a = [1,'23',6,'hek']

console.log(String(a));

let b = {
    name: 'jack'
}
console.log(toString(b));  // ToString(b)  ==>   //ToPrimitive(b, String)

console.log('12wdfgew'.toString());

image.png

2.转数字

与转字符串有所不同的是,v8引擎在调用ToNumber()函数并且发现传入的参数是对象然后交给ToPritimive(obj,Number)处理时,它的 ToPritimive(obj,Number)代码执行逻辑与 ToPrimitive(obj, String)有一些差别,如下:

ToPrimitive(obj, Number)

  • 如果 obj 是原始类型,直接返回
  • 否则,调用 valueOf(),如果得到原始类型,直接返回 //valueOf() 判断包装类
  • 否则,调用 toString(),如果得到原始类型,直接返回
  • 否则,报错

差别就是调用 valueOf() 和 调用 toString()的先后顺序不同。那这个时候有些小伙伴就会有疑问了,valueOf() 是一个什么样的函数。valueOf() 唯一的作用就是可以返回包装类里的值,让我们用一个例子来理解它:

let a1 = {}
let a2 = []
let a3 = [1,2,3]
let a4 = {
    name: 'Mary'
}
console.log(a1.valueOf());
console.log(a2.valueOf());
console.log(a3.valueOf());
console.log(a4.valueOf());

image.png 那为什么转字符串与转数字的第二步和第三步顺序相反呢?

  • 使用 "String" 作为提示时,它首先尝试将对象转换为字符串。这是因为在大多数情况下,当你需要一个字符串时,从字符串表示的理解上看,toString() 更符合用户的预期。
  • 使用 "Number" 提示时,则是首先尝试 valueOf(),因为当处理数字或数值计算时,优先获取对象的数值表示(如果有)是合乎逻辑的。

3.转布尔

任何对象转布尔返回的都是true,大家可以自行尝试。

隐式类型转换的场景

  1. 四则运算 + - * / %
  2. 判断语句 if while == > < >= <=

加法运算 (+)

  • 如果其中一个操作数是字符串,JavaScript 会将另一个操作数转换为字符串并进行字符串拼接。
console.log(5 + "3"); // 输出: "53" (数字 5 被转换为字符串 "5")
console.log("Hello " + 5); // 输出: "Hello 5"
  • 如果两个操作数都是数字,结果就是它们的和。
console.log(5 + 3); // 输出: 8

其他运算符 (-*/%)

  • 在进行减法、乘法、除法和取余运算时,如果涉及到字符串,JavaScript 会首先尝试将字符串转换为数字。
console.log(5 - "3"); // 输出: 2 (字符串 "3" 被转换为数字 3)
console.log("5" * "2"); // 输出: 10 (字符串 "5" 和 "2" 被转换为数字)
console.log(10 / "2"); // 输出: 5
console.log(10 % "3"); // 输出: 1
  • 如果字符串不能被转换为有效的数字(例如非数字字符的字符串),则结果是 NaN
console.log(5 - "abc"); // 输出: NaN

在进行条件判断时(如在 if 和 while 语句中),JavaScript 会执行隐式类型转换,将条件表达式的结果转换为布尔值。

if 和 while

let value = 0;

if (value) {
    console.log("This won't be executed.");
} else {
    console.log("This will be executed."); // 输出: "This will be executed."
}

// 注意: 0, "", null, undefined, 和 NaN 都会被转换为 false,其他值会被转换为 true

使用比较运算符 (==><>=<=)

  • 相等运算符 (==)  会进行类型转换,因此它比较的是值而不是类型。
console.log(5 == "5"); // 输出: true (字符串 "5" 被转换为数字 5)
console.log(0 == false); // 输出: true
console.log(null == undefined); // 输出: true
  • 严格相等运算符 (===)  不会进行类型转换。
console.log(5 === "5"); // 输出: false (不同类型)
console.log(0 === false); // 输出: false
  • 其他比较运算符:在使用大于小于比较操作时,JavaScript 会先将操作数转换为数字,然后再进行比较。
console.log("5" > 2); // 输出: true (字符串 "5" 被转换为数字)
console.log("10" < 2); // 输出: false

一元运算符 +

当你使用一元 + 运算符时,JavaScript 执行以下步骤:

  1. 类型检查:首先确定操作数的类型。

  2. 转换

    • 如果操作数是数字,则返回这个数字本身。
    • 如果操作数是字符串,JavaScript 会尝试将其转换为数字。如果字符串可以成功转换,返回相应的数字;如果不能(例如字符串为 "abc"),则返回 NaN(Not a Number)。
    • 如果操作数是布尔值,true 会被转换为 1false 会被转换为 0
    • 如果操作数是 null,会转换为 0
    • 如果操作数是 undefined,会转换为 NaN

示例代码

// 正号运算符,保持数字不变
let num1 = +5; // 输出: 5
console.log(num1);

let num2 = +"10"; // 字符串 "10" 转换为数字 10
console.log(num2); // 输出: 10

let str1 = +"abc"; // 无法转换,结果是 NaN
console.log(str1); // 输出: NaN

let bool1 = +true; // true 转换为 1
console.log(bool1); // 输出: 1

let bool2 = +false; // false 转换为 0
console.log(bool2); // 输出: 0

let nullValue = +null; // null 转换为 0
console.log(nullValue); // 输出: 0

let undefinedValue = +undefined; // undefined 转换为 NaN
console.log(undefinedValue); // 输出: NaN

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

val1 + val2

lprim = ToPrimitive(val1)

eprim = ToPrimitive(val2)

  • 如果 lprim 或者 rprim 是字符串,另一个值直接被 ToString()
  • 否则,返回对 ToNumber(lprim) 和 ToNumber(rprim) 应用加法运算的结果

==

我们直接看官方文档的定义

image.png


那学习到这里,我们再回到开头的那个问题,[] == ![]返回的是true还是false呢?相信大家也已经知道了答案,那就是true。 根据上面的官方文档,我们可以进行推理,如下:

[] == ![]
--> [] == !true

--> [] == false

--> [] == 0

--> '' == 0

--> 0 == 0

--> true

那看到这里,我相信你已经完全掌握了js中的转换机制了

20200229174423_bzukt.jpg