js学习日记-隐式转换相关的坑及知识
隐式转换比较是js中绕不过去的坎,就算有几年经验的工程师也很有可能对这块知识不够熟悉。就算你知道使用===比较从而避免踩坑,但是团队其它成员不一定知道有这样或那样的坑,请先在纸上写出下面代码会输出什么开始我们今天的知识总结。
1 2 3 | console.log( new String( 'abc' )== true ) console.log({}== true ) console.log([]==![]) |
如果您的答案是:true true false,那么恭喜你,你全部都答错了,你的思路可能如下:
1.new String('123')创造出来的是一个字符串,非空字符串转成布尔值是true,true==true,结果自然为true
2.{}是一个字面量对象,对象转换成布尔值是true,true==true,结果自然为true
3.右侧有!运算符,优先级最高,先转右边。数组是一个对象,对象转换成布尔值结果是true,![]的结果为false,要比较的表达式变成了[]=false,然后数组转成布尔值为true,true==false,结果是false
为什么你一个题都做不对呢?如果你很有可能是你没有掌握到js中对象的概念,null的特殊规则,以及==运算符的转换规则,下面就开始展开相应的知识。
回顾链接:javascript的数据类型
一、变量采用字面量形式、包装器方式,new 方式的区别
var a='xxx' 申明的是一个string类型,它是一个基本类型
var a=String('abc'), String()是一个包装类,用于将参数转换成string类型
var a=new String('abc') 当在函数前采用new时,相当于创建了一个object类型
1 2 3 4 | var a= 'abc' console.log( typeof a) //string console.log( typeof String( 'abc' )) //string console.log( typeof ( new String( 'abc' ))) //object |
哪些类型是object类型呢?用new 方法创建出来的肯定是object类型,除此之外,对象字面量(即{ }) 数组、日期、正则表达式、Math、函数表达式、函数指针,都是object类型。
这里又涉及到一个容易混的地方,有同学说var a=function(){}和function a(){} 我用typeof输出时明明是function啊,你怎么说是object类型呢?因为采用函数表达式或函数声明时,实际上JS引擎在后面做了一个工作,相当于调用了new Function('参数1','参数2',''函数体),使用new创建的,肯定就是object类型了,至于typeof对函数返回的是function,这是一个特例。
二、各种类型隐式转换到布尔类型对照表
数据类型 | 转换为true的值 | 转换为false的值 |
Boolean | true | false |
String | 任何非空字符串 | "" (空字符串) |
Number | 任何非零数字值(包括无穷大) | 0和NaN |
Object | 任何对象 | null |
Undefined | 不适用 | undefine
|
三、逻辑!的转换规则
使用该符号时,首先将操作符后面的数据转成布尔类型,然后再取反,没什么特殊的。这个运算符有一个很经典的应用。
1 2 | var a; var b=!!a; |
这段代码的作用就是变量b的值只能为true或false,当a为undefined时b的值为false,否则为true
当然也可以采用var b=a || false来达到相同的效果,我个人更喜欢后面这种。
这背后的原理正是应用了上面第三大点的知识,所以说牢牢理解隐式转换是非常有必要的
四、相等比较==的规则
比较操作符会为两个不同类型的操作数转换类型,然后进行严格比较。当两个操作数都是对象时,JavaScript会比较其内部引用,当且仅当他们的引用指向内存中的相同对象(区域)时才相等,即他们在栈内存中的引用地址相同。
为了以防被博客上看到的一些总结误导,我特地查询了MDN上对类型转换规则的说明:
- 当比较数字和字符串时,字符串会转换成数字值。 JavaScript 尝试将数字字面量转换为数字类型的值。
- 如果其中一个操作数为布尔类型,那么先将布尔类型转换为数字类型。
- 如果一个对象与数字或字符串向比较,JavaScript会尝试返回对象的默认值。操作符会尝试通过方法valueOf和toString将对象转换为其原始值(一个字符串或数字类型的值)。如果尝试转换失败,会产生一个运行时错误。
- 注意:当且仅当与原始值比较时,对象会被转换为原始值。当两个操作数均为对象时,它们作为对象进行比较,仅当它们引用相同对象时返回true。
从上面这段权威的规则可以得出以下结论:
1.如果两边类型不一致,但两边都属于string、number、boolean中的一种,则首先尝试将两边都转化成数字类型来比较(上面第1、2条总结起来)
1 2 | var a= '123' console.log(a== false ) //false,'123'转成数字是123,右侧转成数字是0,最终比较123==0 |
2.如果一边是object类型,另一边是string或number(boolean类型也会转换成数字),则先看object类型能不能转成数字,如果不能转成数字,则转成字符串
1 2 | var a= new String(123) console.log(a==123) //true,a.valueOf()结果就是数字123,最终比较的是123==123 |
1 2 3 4 5 6 7 | var a={} console.log(a==1) //上面a==1在js解释引擎中的执行过程如下: //a.valueOf()获取到的不是基本类型,调用a.toString()得到'[object Object]' '[object Object]' ==1; //两边类型不致,左侧转成数字 NaN==1; //false,NaN跟任何类型比较都为false |
上面两条得出一个规律,相等比较就是把左边和右边转成number类型来比较
3.如果两边都是引用类型,不进行转换,直接比较内存中引用的是否是同一个地址。
1 | console.log([]==[]) //false,指针指向的地址不同 |
4.null和undefined跟其它任何类型比较时,不做转换,也就是跟其它类型比较时结果总是为false,但undefined==null的结果总是为true
5.NaN跟任何类型比较都为false,NaN==NaN的结果也为false
搞清楚了上述规则,开始那几个题就特别简单了,而且万变不离其宗。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | console.log( new String( 'abc' )== true ) //step1:右侧转成数字 new String( 'abc' )==1 //step2 new String('abc').valueOf()不是数字也不是字符串,再调用toString() '[object Object]' ==1 //step3:字符串转数字 NaN==1 //false,NaN和任何类型比较都为false<br> console.log({}== true ) //step1:右侧转成数字 {}==1 //step2 {}.valueOf()不是数字也不是字符串,再调用toString() '[object Object]' ==1 //step3:字符串转数字 NaN==1 //false,NaN和任何类型比较都为false<br> console.log([]==![]) //step1:!优先级比==高,先转右边,[]是对象类型,转成布尔值为true,!true就是false []== false //step2:右侧转成数字为0 []==0 //step3:左侧是一个对象,valueOf转出来不是字符也不是字符串,调用toString(),得到空字符串 '' ==0 //step4:字符串转成数字 0==0 //true |
五、大于或小于符的坑
先来看一个又会让你大吃一惊的例子
1 | console.log( '23' < '3' ) //true |
如果你回答是false,那么你又该看一看了,为什么会是true呢?因为字符串类型比较大小时,不是进行隐式转换,而是逐位比较ascii码,第1位不同则返回结果,否则继续比较第2位,直到某一位不同为止,上面的例子js引擎是这么来处理的
var a='23'.charCodeAt(0) //50
var b='3'.charCodeAt(0) //51
50<51 //false
比较大小时如果左右两边是变量,建议最好先转成数字来比较。
六、加号的转换
1. 加号前后如果有一个为字符串的话,则把另一个转成字符串(js引擎在后面调用了toString()方法),然后做连接操作
2. 如果不满足上一条的话则转成数字,假设要转换的对象为obj,转换规则如下:
- 如果obj为原始值,直接返回;
- 否则调用 obj.valueOf(),如果执行结果是原始值,返回之;
- 否则调用obj.toString(),如果执行结果是原始值,返回之;
- 否则抛异常。
true + 1 // 2,布尔型转化为了数字 [] + 1 // '1',空数组转化为了字符串 {} + 1 // '[object Object]1', var now = new Date() typeof (now + 1) // 'string',日期对象转化为了字符串
7⃣️valueOf和stringOf
对象 | 返回值 |
---|---|
Array | 返回数组对象本身。 |
Boolean | 布尔值。 |
Date | 存储的时间是从 1970 年 1 月 1 日午夜开始计的毫秒数 UTC。 |
Function | 函数本身。 |
Number | 数字值。 |
Object | 对象本身。这是默认情况。 |
String | 字符串值。 |
Math、 Error、 undefined、Null 对象没有 valueOf 方法。 |
对象 | 返回值 |
---|---|
Array | 以逗号分隔每个数组元素形成的字符串 |
Boolean | “true”或"false" |
Date | Thu Jul 06 2017 00:00:00 GMT+0800 |
Function | 函数的文本定义 |
Number | “123” |
Object | [object Object] |
String | 字符串值本身 |
Math、 Error、 undefined、Null 对象没有 valueOf 方法。 |
在字符串运算里,会优先调用toString(),如alert(c)。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!