你不知道的JavaScript——类型转换篇

最近,我在学习中碰到了一些类型转换的难题。为此,我请教了老师且翻阅了一些书籍。想要彻底搞懂它。它并没有想象中的那样简单,其中还有一些你不知道的转换逻辑,以下便是我对其的学习分享。

我们先来看一道经典的面试题:[]==![]返回true还是false,为什么?这个问题直击JavaScript类型转换的核心,展现了语言中一些微妙且易混淆的特性。要解开这个谜团,我们需要深入理解JavaScript中类型转换的机制,尤其是如何在比较操作中发挥作用。

在分析这个问题之前,我们先来温习一下基础知识

类型转换基础

在JavaScript中,类型转换大致可分为两类:显式转换和隐式转换。显式转换是我们主动通过函数如Boolean()Number()String()进行的,而隐式转换则是由JavaScript引擎在特定情境下自动执行的,比如比较操作符的使用。

原始值转原始值——显式类型转换

  1. 转Boolean ————Boolean()
  2. 转Number ————Number()
  3. 转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。所以,最终""==0true

大致过程:
[] == ![]->[] == !true->[] == false->[] == 0->Number([]) == 0->‘’ == 0->0 == 0

总结

[]==![]返回true,这一结果揭示了JavaScript中类型转换的复杂性和微妙性。理解这些转换规则不仅有助于解决面试难题,更是日常开发中避免类型错误、提升代码质量的关键。通过本文的深入解析,希望你能对JavaScript的类型转换机制有更深的认识,让那些“未知”的角落变得清晰明了。

  • 29
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值