前言:当代码开始"变脸"——类型转换的魔法与陷阱
在JavaScript的世界里,类型转换就像一场精彩的魔术表演:数字突然变成字符串,对象神秘地化为原始值,空数组竟能与0相等...这些看似魔法的行为背后,隐藏着严谨的语言规则。理解类型转换机制,是解锁JavaScript深层奥秘的关键钥匙。
作为前端开发者,你是否曾困惑于:
- 为什么
[] == ![]的结果是true? - 为什么
{} + []返回0而[] + {}返回"[object Object]"? ==和===到底该在什么场景使用?
本文将深入剖析JavaScript的类型转换机制,通过生动的代码示例和原理图解,带你:
- 揭开
==与===的神秘面纱 - 掌握显示与隐式类型转换的规则
- 破解
ToPrimitive算法的运作原理 - 理解
valueOf()和toString()的转换优先级 - 识别日常开发中的类型转换陷阱
准备好破解JavaScript的"变形魔法"了吗?让我们开始这段类型转换的探索之旅! 前置知识:类型判断(toString原理)
== vs ===
- == 会发生隐式类型转换,所以只判断值是否相等
- === 不会发生隐式类型转换,所以判断值和类型是否相等
let a=1
let b=2
let c='1'
console.log(a==b);
console.log(a==c);
console.log(a===c);
类型转换
- 显示类型转换(原始数据类型之间转换)
-
转布尔 Boolean(x)
-
转数字 Number(x) es5.github.io/#x15.7.1.1
-
转字符串 String(x)
let a = 1;
let b = String(a);
let c = Boolean(a);
let d = "123";
let e = Number(d);
let f = Boolean(d);
console.log(a, b, c, e, f);
console.log(Boolean()); //false
console.log(Boolean(undefined)); //false
console.log(Boolean(null)); //false
console.log(Boolean(0)); //false
console.log(Boolean(NaN)); //false
console.log(Boolean("")); //false
console.log(Boolean("pp")); //true
console.log(Number(d)); //123
console.log(Number("")); //0
console.log(Number("ppp")); //NaN
console.log(Number(null));
console.log(Number(false));
console.log(Number(true));
[相关链接](JavaScript 类型转换)
- 隐式类型转换
-
原始类型转原始类型
-
引用类型转原始类型
-
转布尔 ---任何引用类型转布尔 都是true
-
转字符串 ---String(obj) ==>ToString(obj)==>ToPrimitive(obj,String)Toprimtive只针对Objcet类型
-
转数字 ---Number(obj) ==>ToNumber(obj)==>ToPrimitive(obj,Number)
-
引用类型转字符串:
let arr=[1,2,3,4]
console.log('hi'+arr);
Toprimitive
-
Toprimitive(obj,String)
-
1.判断obj是否为原始类型,是则直接返回
3.否则,调用toString()方法,如果得到原始类型,则返回
5.否则,调用valueOf()方法,如果得到原始类型,则返回
7.否则,抛出typeError异常
-
ToPrimitive(obj,Number)
1.判断obj是否为原始类型,是则直接返回
3.否则,调用valueOf()方法,如果得到原始类型,则返回
5.否则,调用toString()方法,如果得到原始类型,则返回
7.否则,抛出typeError异常
valueof()
- valueOf在对象的原型上(valueof()存在于object.prototype上面),它只能将包装类的对象转化为原始类型
包装类对象是指:这里我们想通过自带构造函数创建原始类型,但是通过构造函数创建时相当于创建对象,这个对象就叫包装类,valueof()可以将我们传入构造函数原始数据拿出来,相当于将包装类对象转换为原始类型。
三大类toString()
- js中大部分的构造函数原型上都重写了toString方法(除了Set,Map)
1.{}.toString() 返回由'[objce'和[[class]]和 ']' 组成的字符串
2.[].toString() 返回由所有元素用逗号连接组成的字符串
3.xxx.toString() 直接返回xxx的字符串字面量(xxx其余数据类型)
console.log(1==[])
以上代码隐式转换过程:
Number([])==>toNumber([])==>toPrimitive([].number)
toPrimitive([],number):
valueof([])==>toString([]) 字符串的toString():返回数组所有元素逗号拼接的字符串,这里返回空字符串
return 原始数据类型''
原始类型的隐式转换:空字符串转数字为0
结果为false
发生隐式类型转换的场景有哪些
1.四则运算 +- * / % 2.判断语句 if while == >= <= != > <
+
- 作为一元运算符 会发生隐式类型转化,转成number
- 作为二元运算符 只要加号左右两边有一个字符串,那么另一个也会转换为字符串 拼接
经典面试题:
[]==![]
[]==!true
[]==false
[]==0
''==0
0==0
v8执行以上几步,结果是true 有!将[]隐式转换为布尔类型,任何引用类型转布尔类型都是true,!ture就是false,因为==是判断值相等,所以等号两边会转化为数字类型,右边就是false转换为数字0,左边就是引用类型隐式转换为数字类型,执行Number() toNumber() toPrimitive() valueof() toString(),数组的toString()方法将数组转换为由逗号拼接的字符串,那么空数组转化为空字符串,空字符串又隐式转换为数字0
{}+[]
一开始的目标是向数字类型转化,因为有加号做四则运算,那么左边对象隐式类型转换为数字类型,先Number() 再到里面的toNumber() 再到里面的toPrimitive , 最后 到toPrimitive()里面的valueof(),再执行object类型的toString()方法返回的是字符串,此时左边为字符串,加号作为二元运算符 只要加号左右两边有一个字符串,那么另一个也会转换为字符串 拼接 ,右边也直接隐式转换为字符串,执行那三步,结果为'',最后字符串拼接
结语:掌握类型转换,编写更健壮的代码
经过本文的探索,我们揭开了JavaScript类型转换的神秘面纱:
==与===的选择:在需要精确控制的场景使用===,在允许灵活转换时使用==- 隐式转换的陷阱:特别注意数学运算和比较运算中的自动转换
- 对象转换的规则:牢记
ToPrimitive算法中valueOf和toString的调用顺序