个人JS日记-隐式转换相关知识

隐式转换比较是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

1、变量采用字面量形式、包装器方式,new 方式的区别

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

2、JavaScript数据类型

使用相等于符时基本类型和object类型的规则不一样,因此咱们有必要再次回顾一下js中的数据类型。 JavaScript有六种基本数据类型:string、boolean、number、null、undefined、symbol(es6新添加),还有一种复杂数据类型:object
按照数据在内存中的存储方式,可将数据类型划分为值类型和引用类型。值类型的数据是存在栈中的,而引用类型则是在栈中会有一个指针,好比对象名,函数名等,它指向堆中的数据。基本数据类型都是值类型,object类型是引用类型,下面是存储示意图。 面试

image
知道了这个知识点后,之后遇到深拷贝问题就很好理解了。

3、各类类型隐式转换到布尔类型对照表

为何要忽然列这个呢,由于使用频率比较高,面试时常常会出现相关的题目,因此须要牢记。好比有一个变量a,咱们须要判断变量a不为undefined时才执行代码,若是你不知道隐式转换,你颇有可能会写成if(a===undefined),这是没问题的,关键是可贵敲,并且多了几个字节出来。我这里直接摘抄了js高程设计上的表。 正则表达式

convert.png

4、!的转换规则

使用该符号时,首先将操做符后面的数据转成布尔类型,而后再取反,没什么特殊的。这个运算符有一个很经典的应用,强制让一个变量的值只能为true或false小程序

var a;
var b=!!a;
复制代码

这段代码的做用就是变量b的值只能为true或false,当a为undefined时b的值为false,不然为true,固然也能够采用var b=a || false来达到相同的效果,我我的更喜欢后面这种。 这背后的原理正是上面的第三条,因此说紧紧理解隐式转换是很是有必要的后端

5、==的转换规则

比较操做符会为两个不一样类型的操做数转换类型,而后进行严格比较。当两个操做数都是对象时,JavaScript会比较其内部引用,当且仅当他们的引用指向内存中的相同对象(区域)时才相等,即他们在栈内存中的引用地址相同。  为了以防被博客上看到的一些总结误导,我特意查询了MDN上对类型转换规则的说明:

  1. 当比较数字和字符串时,字符串会转换成数字值。 JavaScript 尝试将数字字面量转换为数字类型的值。
  2. 若是其中一个操做数为布尔类型,那么先将布尔类型转换为数字类型。
  3. 若是一个对象与数字或字符串向比较,JavaScript会尝试返回对象的默认值。操做符会尝试经过方法valueOf和toString将对象转换为其原始值(一个字符串或数字类型的值)。若是尝试转换失败,会产生一个运行时错误。
  4. 注意:当且仅当与原始值比较时,对象会被转换为原始值。当两个操做数均为对象时,它们做为对象进行比较,仅当它们引用相同对象时返回true。

官方的文档读起来老是有一些拗口,翻译成大白话再加上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使用一套制做,其它的使用另外一套制度)

6、大于或小于符

先来看一个可能会让你条件反射般陷入思惟误区的坑

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码来肯定前后顺序。

7、+号规则

+号运算符便可以对两个数相加,也能够链接字符串,那若是是[1,2,3]+4这种状况下又会发生什么呢?这就须要咱们了解相应的规则,为了方便描述,咱们把+号左侧的值叫作A,右侧的叫作B:
第一步:若是A和B都是number类型,直接相加;
第二步:接下来看A或B中是否有一个是否为string类型,若是有,则将另外一个也转成字符串,而后链接
第三步:既不是number,也不是string,则按以下规则转换:
1.能转换成数字,返回之
2.不然调用valueOf(),若是执行结果是基本类型,返回之;
3.不然调用toString(),若是执行结果是基础类型,返回之;
4.没法获得原始值,抛异常。

valueOf和toString参考.png
相关文章
相关标签/搜索