你不知道的JavaScript——类型转换篇
最近,我在学习中碰到了一些类型转换的难题。为此,我请教了老师且翻阅了一些书籍。想要彻底搞懂它。它并没有想象中的那样简单,其中还有一些你不知道的转换逻辑,以下便是我对其的学习分享。
我们先来看一道经典的面试题:
[]==![]
返回true
还是false
,为什么?这个问题直击JavaScript类型转换的核心,展现了语言中一些微妙且易混淆的特性。要解开这个谜团,我们需要深入理解JavaScript中类型转换的机制,尤其是如何在比较操作中发挥作用。
在分析这个问题之前,我们先来温习一下基础知识
类型转换基础
在JavaScript中,类型转换大致可分为两类:显式转换和隐式转换。显式转换是我们主动通过函数如Boolean()
、Number()
、String()
进行的,而隐式转换则是由JavaScript引擎在特定情境下自动执行的,比如比较操作符的使用。
原始值转原始值——显式类型转换
- 转Boolean ————Boolean()
- 转Number ————Number()
- 转String ————String()
声明这些原始变量,方便下面使用
let s = "s"
let n = 123
let f = false
let u = undefined
let nu = null
原始值转Number
console.log(Number('123')); // 123
console.log(Number('123abc')); // NaN
console.log(Number('')); // 0 空字符串转换为0
console.log(Number('0x00')); // 0 16进制转换为10进制为0
console.log(Number(true)); // 1 布尔值true转换为数字1);
console.log(Number(false)); // 0 布尔值false转换为数字0);
console.log(Number(undefined)); // NaN undefined转换为NaN);
console.log(Number(null)); // 0 null转换为数字0);
console.log(Number([])); // 0 数组转换为数字0);
原始值转Boolean
console.log(Boolean(s)); // true
console.log(Boolean("")); // false
console.log(Boolean(n)); // true
console.log(Boolean(0)); // false 数字里面,只有0是false,其他都是true
console.log(Boolean(-1)); // true
console.log(Boolean(Infinity)); // Infinity无穷大也是true
console.log(Boolean(-Infinity)); // -Infinity负无穷大也是true
console.log(Boolean(NaN)); // false NaN是not a number,所以是false
console.log(Boolean(u)); // false undefined是false
console.log(Boolean(nu)); // false null是false
console.log(Boolean()); // false 空参数是false
原始值转String
console.log(String(n)); // '123'
console.log(String(f)); // 'false'
console.log(String(u)); // 'undefined'
console.log(String(nu)); // 'null'
console.log(String(Infinity)); // 'Infinity'
console.log(String(-Infinity)); // '-Infinity'
console.log(String(NaN)); // 'NaN'
console.log(String({})); // '[object Object]'
console.log(String([])); // '[Obeject Array]'
对象转原始值——隐式类型转换
对象转Number
Number({})
-
先调用
ToNumber(x)
,该函数中会再调用ToPrimitive()
将对象转换为原始值 -
Toprimitive(obj, Number)
- 判断接收到的值是不是原始类型,是则返回
- 否则,调用valueOf()方法,如果得到了原始值,则返回
- 否则,调用toString()方法,如果得到了原始值,则返回
- 转换顺序:valueOf() -> toString()
- 报错
示例分析
console.log(Number({}));
// ToNumber({})
// ToNumber({}, Number)
// valueOf() // [object Object].valueOf() -> {}
// toString({}) // [object Object].toString() -> '[object Object]'
// ToNumber('[object Object]') // NaN
这是对象转数字,那么我们来看看数组转数字,
console.log(Number([])); // 0
欸,它输出的是零,如果按照上面的分析,这不对啊,它应该也是输出NaN才对啊,其实步骤是没问题的,只是数组有些特殊。它自带toString 方法而它的toString方法会将数组内的内容转换成字符串,按上面的例子就是 " "
,就是字符串转数字就是0
对象转String
String({})
-
先调用ToString(x),该函数中会再调用Toprimitive()将对象转换为原始值
-
Toprimitive(obj, String)
- 判断接收到的值是不是原始类型,是则返回
- 否则,调用
toString()
方法,如果得到了原始值,则返回 - 否则,调用
valueOf()
方法,如果得到了原始值,则返回- 转换顺序:toString() -> valueOf()
- 报错
示例分析
console.log(String({}));
// ToString({})
// ToPrimitive({}, String)
// toString({}) // [object Object]
// ToString('[object Object]') // [object Object]
对象转Boolean
任何对象转Boolean时,都返回true
原始值与对象值
当比较操作涉及原始值(如数字、字符串、布尔值)与对象值时,对象需要通过valueOf()
和toString()
方法转换为原始值。这一过程遵循特定的规则,如优先尝试valueOf()
,如果不返回原始值,则尝试toString()
。此外,对象到布尔值的转换总是返回true
。
一元与二元操作符的隐式转换
一元操作符如+
(正号)在用于非数字时会尝试将其转换为数字。而二元操作符如+
(加号)在操作数类型不匹配时,会尝试将两边都转换为字符串(除非一边是数字和一边是字符串,这时会尝试将字符串转为数字进行加法运算)。
一元操作符 +
- +'1' 得到数字1
- '+' 会触发隐式类型转换,往Number转
如:
console.log(+[]);
// Number([])
// ToPrimitive([],Number)
// Number("")
console.log(+{});
// Number({})
// ToPrimitive({},Number)
// Number('[object Object]')
二元操作符
-
'1' + 2 得到字符串 '12'
-
- 会触发隐式类型转换,往String转
-
-
对象 + 对象 得到字符串
- 转换顺序:valueOf() -> toString()
如:
1 + '1' // '11'
console.log(1 + []);
// 1 + ''
console.log({} + []);
// "[object Object]" + ''
等号与严格等号
在JavaScript中,==
(双等号)会进行类型转换以达到可比较的状态,而===
(三等号)则要求两边类型和值都完全相同,不进行任何类型转换。
1 == '1'
// 1 == Number('1')
// 1 == 1
解析经典面试题:[]==![]
经过上面内容的分析,现在,让我们回到开始的问题。首先,解析![]
。这里,!
是一元逻辑非操作符,它会先将[]
转换为布尔值进行取反。由于任何非空对象转换为布尔值都是true
,因此![]
等于false
。
接下来,我们比较[]
和false
。在使用==
进行比较时,由于两边类型不同(一方是数组,另一方是布尔值),JavaScript会进行类型转换。布尔值false
会先转换为数字0
。随后,数组[]
在比较时,会尝试通过valueOf()
和toString()
转换为原始值。数组的valueOf()
返回数组自身,不是一个原始值,所以会继续尝试toString()
,数组的toString()
会将其内容以逗号分隔的字符串形式返回,即""
(空字符串)。
因此,问题简化为""==0
。字符串与数字的比较中,字符串会被转换为数字。空字符串""
转换为数字是0
。所以,最终""==0
为true
。
大致过程: [] == ![]->[] == !true->[] == false->[] == 0->Number([]) == 0->'' == 0->0 == 0
总结
[]==![]
返回true
,这一结果揭示了JavaScript中类型转换的复杂性和微妙性。理解这些转换规则不仅有助于解决面试难题,更是日常开发中避免类型错误、提升代码质量的关键。通过本文的深入解析,希望你能对JavaScript的类型转换机制有更深的认识,让那些“未知”的角落变得清晰明了。
转载自:https://juejin.cn/post/7377683176228995099