我们先来看一个很经典的面试题:[] == ![],这个判断返回的是true还是false呢?大家可能第一时间都无法得出答案,而这个问题就涉及到了JS中的类型转换机制,今天我们就来带着上面提到的这个问题一起探讨一下JS的类型转换机制。
== VS ===
在我们日常判断同类型的数据是否相等时,用 == 或者 === 起到的效果都一样。如下图:
但当两边的数据类型不一样时,这两个判断方式就有差别了。让我们看个例子:
那这又是为什么呢?这就是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));
原始类型转布尔:
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'));
原始类型转数字:
对于布尔类型:true返回1,false返回2
对于null:返回0
对于undefined:返回NaN
对于字符串类型:如果该字符串中只有数字类型,那就返回这个数字,如果包含了除数字以外的东西,比如英文字母,标点符号等,都返回NaN
3.转字符串
原始类型转字符串就比较的简单粗暴了,直接将传入的参数用引号括起来返回字符串即可,大家这里可以自行敲一些代码进行尝试
对象转原始值
这里我们直接对着js官方文档进行分析
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());
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());
那为什么转字符串与转数字的第二步和第三步顺序相反呢?
- 使用
"String"作为提示时,它首先尝试将对象转换为字符串。这是因为在大多数情况下,当你需要一个字符串时,从字符串表示的理解上看,toString()更符合用户的预期。 - 使用
"Number"提示时,则是首先尝试valueOf(),因为当处理数字或数值计算时,优先获取对象的数值表示(如果有)是合乎逻辑的。
3.转布尔
任何对象转布尔返回的都是true,大家可以自行尝试。
隐式类型转换的场景
- 四则运算 + - * / %
- 判断语句 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 执行以下步骤:
-
类型检查:首先确定操作数的类型。
-
转换:
- 如果操作数是数字,则返回这个数字本身。
- 如果操作数是字符串,JavaScript 会尝试将其转换为数字。如果字符串可以成功转换,返回相应的数字;如果不能(例如字符串为
"abc"),则返回NaN(Not a Number)。 - 如果操作数是布尔值,
true会被转换为1,false会被转换为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) 应用加法运算的结果
==
我们直接看官方文档的定义
那学习到这里,我们再回到开头的那个问题,[] == ![]返回的是true还是false呢?相信大家也已经知道了答案,那就是true。
根据上面的官方文档,我们可以进行推理,如下:
[] == ![]
--> [] == !true
--> [] == false
--> [] == 0
--> '' == 0
--> 0 == 0
--> true
那看到这里,我相信你已经完全掌握了js中的转换机制了