隐式转换比较是js中绕不过去的坎,就算有几年经验的工程师也颇有可能对这块知识不够熟悉。就算你知道使用===比较从而避免踩坑,可是团队其它成员不必定知道有这样或那样的坑,有后端语言经验的人经常会造成一个思惟误区:“JS这门语言很简单,看看语法再找几个例子就能操做DOM,编写特效了”。随着react、vue、html5等技术在项目中大规模使用,愈来愈多的项目中使用了大量的JS,甚至整个项目都用JS来写(例如基于webapi的SPA管理后台、微信小程序、微信H5应用,Hybrid app),若是不深刻的去学习JS,不改变思惟误区,那么你的程序颇有可能在这些地方产生BUG,这种隐藏的bug若是你对js不熟悉的话会很是难以查找。 下面开始今天的讨论,请先在纸上写出你的答案。html
console.log(newString('abc')==true)
console.log({}==true)
console.log([]==![])
复制代码
若是您的答案是:true true false,那么恭喜你,你所有都答错了,你的思路可能以下:vue
1.new String('123')创造出来的是一个字符串,非空字符串转成布尔值是true,true==true,结果天然为true
2.{}是一个字面量对象,对象转换成布尔值是true,true==true,结果天然为true
3.右侧有!运算符,优先级最高,先转右边。数组是一个对象,对象转换成布尔值结果是true,![]的结果为false,要比较的表达式变成了[]=false,而后数组转成布尔值为true,true==false,结果是falsehtml5
为何你一个题都作不对呢?若是你颇有多是你没有掌握到js中对象的概念,null的特殊规则,以及==运算符的转换规则,下面是涉及到的一些知识。react
var a='xxx' //申明的是一个string类型,它是一个基本类型
var a=String('abc') // String()是一个包装类,用于将参数转换成string类型
var a=new String('abc') //采用new方式时建立了一个object类型
复制代码
使用typeof验证上面的结论es6
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类型呢?为何说函数申明或函数表达式是object类型呢,由于申明的时侯至关于调用了new Function('参数1','参数2',''函数体),使用new建立的确定就是object类型了,至于typeof对函数返回的是function,这是一个历史遗留问题。web
使用相等于符时基本类型和object类型的规则不一样,因此咱们有必要再次回顾一下js中的数据类型。 JavaScript有六种基本数据类型:string、boolean、number、null、undefined、symbol(es6新添加),还有一种复杂数据类型:object。
按照数据在内存中的存储方式,可将数据类型划分为值类型和引用类型。值类型的数据是存在栈中的,而引用类型则是在栈中会有一个指针,好比对象名,函数名等,它指向堆中的数据。基本数据类型都是值类型,object类型是引用类型,下面是存储示意图。 面试
为何要忽然列这个呢,由于使用频率比较高,面试时常常会出现相关的题目,因此须要牢记。好比有一个变量a,咱们须要判断变量a不为undefined时才执行代码,若是你不知道隐式转换,你颇有可能会写成if(a===undefined),这是没问题的,关键是可贵敲,并且多了几个字节出来。我这里直接摘抄了js高程设计上的表。 正则表达式
使用该符号时,首先将操做符后面的数据转成布尔类型,而后再取反,没什么特殊的。这个运算符有一个很经典的应用,强制让一个变量的值只能为true或false小程序
var a;
var b=!!a;
复制代码
这段代码的做用就是变量b的值只能为true或false,当a为undefined时b的值为false,不然为true,固然也能够采用var b=a || false来达到相同的效果,我我的更喜欢后面这种。 这背后的原理正是上面的第三条,因此说紧紧理解隐式转换是很是有必要的后端
比较操做符会为两个不一样类型的操做数转换类型,而后进行严格比较。当两个操做数都是对象时,JavaScript会比较其内部引用,当且仅当他们的引用指向内存中的相同对象(区域)时才相等,即他们在栈内存中的引用地址相同。 为了以防被博客上看到的一些总结误导,我特意查询了MDN上对类型转换规则的说明:
官方的文档读起来老是有一些拗口,翻译成大白话再加上JS高程设计书上说的特殊状况,总结起来就如下几点:
1.转换时若是两边都是引用类型,则直接比较内存中的地址(也就是指针指向的地址)
console.log([]==[]) //false,指针指向的地址不一样
复制代码
2.若是两边类型不一致,则两边都转成number类型,引用类型先调用valueOf()方法,若是能转成数字就OK,不能转成数字的话,就调用toString()转成字符串。
var a='123'
console.log(a==false) //false,'123'转成数字是123,右侧转成数字是0,最终比较123==0
console.log(a==123) //true,右边是数字,直接转换左右便可
复制代码
object类型的比较
var a=new String(123)
console.log(a==123) //true,a.valueOf()结果就是数字123,最终比较的是123==123
复制代码
再来一个例子
var a={}
console.log(a==1)
//上面a==1在js解释引擎中的执行过程以下:
//a.valueOf()获取到的不是基本类型,调用a.toString()获得'[object Object]'
'[object Object]'==1;
//两边类型不致,左侧转成数字
NaN==1;//false,NaN跟任何类型比较都为false
复制代码
3.null、NaN、undefined和string、number、boolean、object类型比较时,都不作隐式转换,比较的结果直接为false。可是须要注意如下几个规则:
console.log(NaN==NaN) //false
console.log(undefined==null) //true
console.log(null==null) //true
console.log(null==undefined) //true
复制代码
搞清楚了上述规则,开始那几个题就特别简单了,并且万变不离其宗。咱们一步一步的来分析一下。
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
console.log({}==true)
//step1:右侧转成数字
{}==1
//step2 {}.valueOf()不是数字也不是字符串,再调用toString()
'[object Object]' ==1
//step3:字符串转数字
NaN==1 //false,NaN和任何类型比较都为false
console.log([]==![])
//step1:!优先级比==高,先转右边,[]是对象类型,转成布尔值为true,!true就是false
[]==false
//step2:右侧转成数字为0
[]==0
//step3:左侧是一个对象,valueOf()转出来不是字符也不是字符串,调用toString(),获得空字符串
''==0
//step4:字符串转成数字
0==0 //true
复制代码
是否是还记不住规则,借用了一下高中政治书上的口吻,就很容易记住了:一个中心(左右两边转换成number为中心),两个基本点(转换条件:1.类型不一样时才转换 2.两边都是引用类型时直接比较地址),一国两制(null、NaN、undefined使用一套制做,其它的使用另外一套制度)
先来看一个可能会让你条件反射般陷入思惟误区的坑
console.log('23'<'3')
复制代码
若是你得出的结果是false,那么你脑子里极有可能对这个表达式进行了一个隐式转换,转成了数字,正确的结果是false,为何呢? 字符串类型比较大小时,不进行类型转换,而是逐位比较ascii码,第1位不一样则返回结果,不然继续比较第2位,直到某一位不一样为止。上面的例子中js引擎在背后作了以下规则(请忽视我把变量分红了两行写,我这里不考虑性能:))
var a='23'.charCodeAt(0) //50
var b='3'.charCodeAt(0) //51
50<51 //false
复制代码
你可能会说哪一个脑残会写这种代码,还真不必定,当<左右两边都是变量时就极有可能产生这种错误,另外一个涉及到比较时的坑就是数组排序。
var a=[1,10,3,100].sort()
复制代码
你指望的结果是[1,3,10,100],但是结果再次让你失望了,缘由是sort()方法默认的比较规则会先把每一个元素转成字符串,而后比较字符串的ascii码来肯定前后顺序。
+号运算符便可以对两个数相加,也能够链接字符串,那若是是[1,2,3]+4这种状况下又会发生什么呢?这就须要咱们了解相应的规则,为了方便描述,咱们把+号左侧的值叫作A,右侧的叫作B:
第一步:若是A和B都是number类型,直接相加;
第二步:接下来看A或B中是否有一个是否为string类型,若是有,则将另外一个也转成字符串,而后链接
第三步:既不是number,也不是string,则按以下规则转换:
1.能转换成数字,返回之
2.不然调用valueOf(),若是执行结果是基本类型,返回之;
3.不然调用toString(),若是执行结果是基础类型,返回之;
4.没法获得原始值,抛异常。