按照正常的逻辑来讲,咱们判断两个值是否相等会遵循如下规则:
可是我看下面一组值:ecmascript
[]==0 //true []==false //true []==!{} //true [10]==10 //true '0'==false //true ''==0 //true undefined==null //true !null==true //true
竟然没有按照咱们的剧本走,那它比较规则又是什么?下面我就来分析一波。spa
首先咱们先去ECMAScript5.1中文版( http://lzw.me/pages/ecmascrip... )找一下“==”的比较规则,以下:prototype
1.若Type(x)与Type(y)相同, 则 a.若Type(x)为Undefined, 返回true。 b.若Type(x)为Null, 返回true。 c.若Type(x)为Number, 则 i.若x为NaN, 返回false。 ii.若y为NaN, 返回false。 iii.若x与y为相等数值, 返回true。 iv.若x 为 +0 且 y为−0, 返回true。 v.若x 为 −0 且 y为+0, 返回true。 vi返回false。 d.若Type(x)为String, 则当x和y为彻底相同的字符序列(长度相等且相同字符在相同位置)时返回true。 不然, 返回false。 e.若Type(x)为Boolean, 当x和y为同为true或者同为false时返回true。 不然, 返回false。 f.当x和y为引用同一对象时返回true。不然,返回false。 2.若x为null且y为undefined, 返回true。 3.若x为undefined且y为null, 返回true。 4.若Type(x) 为 Number 且 Type(y)为String, 返回comparison x == ToNumber(y)的结果。 5.若Type(x) 为 String 且 Type(y)为Number,返回比较ToNumber(x) == y的结果。 6.若Type(x)为Boolean, 返回比较ToNumber(x) == y的结果。 7.若Type(y)为Boolean, 返回比较x == ToNumber(y)的结果。 8.若Type(x)为String或Number,且Type(y)为Object,返回比较x == ToPrimitive(y)的结果。 9.若Type(x)为Object且Type(y)为String或Number, 返回比较ToPrimitive(x) == y的结果。 10.返回 false
看完ECMAScript5.1中文版的介绍以后,相信不少小伙伴的心情应该是这样的:
别看上面说的有点花里胡哨的,其实咱们能够用很简单的话来总结出来。因为本篇文章核心是“==”是如何进行类型转换,我就总结一下类型不一样的状况下“==”是如何比较的。code
具体流程图以下:对象
备注:ip
Javascript的数据类型能够分为如下两种:ci
Boolean | Number |
---|---|
true | 1 |
false | 0 |
若是是标准的数字格式,转换成Number类型相比不用多说,好比下面这几个栗子🌰:rem
"123" => 123 "12.34" => 12.34 "0.12" => 0.12 "-12.34" => -12.34
可是若是是非标准的数据格式,要分两种状况来考虑:字符串
这种状况下会首先进行补0和去0的操做,下面看几个栗子🌰:get
"01234" => 1234 ".1" => 0.1 "12." => 12 "1.000" => 1 "-02.30" => -2.3
这种状况下通通返回NaN,意为“Not a Number”,这里咱们要注意一下,NaN仍是Number类型,下面看几个栗子🌰:
"123aa4" => NaN "哈喽,DD" => NaN typeof NaN => "numer"
在介绍转换规则以前,首先咱们咱们介绍一下引用类型都带有的两个方法:
这两者均可以将引用类型转换成原始数据类型,接下来咱们对两者作一个详细的介绍:
MDN是这样解释的:
The toString() method returns a string representing the object.(toString()这个方法返回一个表明这个对象的字符串)
举个栗子🌰:
const obj = {} console.log(String(obj)) //"[object Object]" obj.toString = function(){ return 'Hello,Teacher Cang!' } console.log(String(obj)) //"Hello,Teacher Cang!"
MDN是这样解释的:
The valueOf() method returns the primitive value of the specified object.( valueOf()这个方法返回这个对象的原始数据值)
举个栗子🌰:
const obj = {} console.log(Number(obj)) //NaN obj.valueOf = function(){ return 12345; } console.log(Number(obj)) //12345
关于这两者的优先级,在不一样的状况下有着不一样的优先级,下面咱们根据不一样状况介绍一下。
看个栗子🌰:
const obj = { toString(){ console.log('调用了toString'); return 'Hello,Teacher Cang!'; }, valueOf(){ console.log('调用了valueOf'); return 12345; } } console.log(Number(obj)); 控制台输出结果: >>>>>>>>>>>>>>>>>> 调用了valueOf 12345
经过上面的代码的运行结果,咱们得出这么一个结论:
在ToNumber状况下,valueOf的优先级大于toString。
接下里咱们看这么一种状况,若是valueOf返回的并非原始数据类型会怎么样。
const obj = { toString(){ console.log('调用了toString'); return 12345; }, valueOf(){ console.log('调用了valueOf'); return {}; } } console.log(Number(obj)); 控制台输出结果: >>>>>>>>>>>>>>>>>> 调用了valueOf 调用了toString 12345
从上面的运行结果中,咱们能够得出这么一个结论:
在ToNumber状况下,若是valueOf返回的不是原始数据类型,则会自动调用toString。
打破砂锅问到底,再来一个很是极端的状况,若是说valueOf和toString返回的都不是原始数据类型,这时又该怎么样呢?一样,咱们继续看一下运行结果:
const obj = { toString(){ console.log('调用了toString'); return {}; }, valueOf(){ console.log('调用了valueOf'); return {}; } } console.log(Number(obj)); 控制台输出结果: >>>>>>>>>>>>>>>>>> 调用了valueOf 调用了toString Uncaught TypeError: Cannot convert object to primitive value
从上面的运行结果中,咱们再次能够得出这么一个结论:
在ToNumber状况下,若是valueOf和toString返回的都不是原始数据类型,那么js会抛出异常,提示没法将引用类型转换原始数据类型。
根据三组代码的运行的结果,咱们最后总结一下:
在ToNumber状况下,js会优先调用valueOf,若是valueOf返回的不是原始数据类型,则会接着调用toString,若是toString返回的也不是原始数据类型,js会抛出一个异常,提示没法将引用类型转换原始数据类型。
具体流程图以下:
看个栗子🌰:
const obj = { toString(){ console.log('调用了toString'); return 'Hello,Teacher Cang!'; }, valueOf(){ console.log('调用了valueOf'); return 12345; } } console.log(String(obj)); 控制台输出结果: >>>>>>>>>>>>>>>>>> 调用了toString Hello,Teacher Cang!
经过上面的代码的运行结果,咱们得出这么一个结论:
在ToString状况下,toString的优先级大于valueOf。
一样咱们看一下,toString返回的值不是原始数据类型时会怎样:
const obj = { toString(){ console.log('调用了toString'); return {}; }, valueOf(){ console.log('调用了valueOf'); return 'Hello,Teacher Cang!'; } } console.log(String(obj)); 控制台输出结果: >>>>>>>>>>>>>>>>>> 调用了toString 调用了valueOf Hello,Teacher Cang!
根据运行结果咱们能够得出:
在ToString状况下,若是toString返回的不是原始数据类型,则会自动调用valueOf。
最后咱们看一下极端状况,两者返回的都不是原始数据类型:
const obj = { toString(){ console.log('调用了toString'); return {}; }, valueOf(){ console.log('调用了valueOf'); return {}; } } console.log(String(obj)); 控制台输出结果: >>>>>>>>>>>>>>>>>> 调用了toString 调用了valueOf Uncaught TypeError: Cannot convert object to primitive value
咱们又发现:
在ToString状况下,若是toString和valueOf返回的都不是原始数据类型,那么js会抛出异常,提示没法将引用类型转换原始数据类型。
咱们将三个结论综合一下:
在ToString状况下,js会优先调用toString,若是toString返回的不是原始数据类型,则会接着调用valueOf,若是valueOf返回的也不是原始数据类型,js会抛出一个异常,提示没法将引用类型转换原始数据类型。
具体流程图以下:
从文章前面咱们总结出来“==”的比较流程来看,引用类型转换成原始数据类型以后,若是是Sting类型的话,还要再次转成Number类型,所以“==”下的引用类型转换原始数据类型过程当中,遵循ToNumber的优先级规则。
const obj = { toString(){ console.log('调用了toString'); return 'Hello,Teacher Cang!'; }, valueOf(){ console.log('调用了valueOf'); return 12345; } } console.log(obj==12345); 控制台输出结果: >>>>>>>>>>>>>>>>>> 调用了valueOf true
const obj = { toString(){ console.log('调用了toString'); return 12345; }, valueOf(){ console.log('调用了valueOf'); return {}; } } console.log(obj==12345); 控制台输出结果: >>>>>>>>>>>>>>>>>> 调用了valueOf 调用了toString true
const obj = { toString(){ console.log('调用了toString'); return {}; }, valueOf(){ console.log('调用了valueOf'); return {}; } } console.log(obj==12345); 控制台输出结果: >>>>>>>>>>>>>>>>>> 调用了toString 调用了valueOf Uncaught TypeError: Cannot convert object to primitive value
“==”号只涉及到了ToPrimitive和ToNumber这两种转换,ToBoolean和ToString这两个没有涉及到的咱们也介绍一下。
对于ToBoolean,咱们只须要记住几个特例是转成false的,其他的皆为true。
Boolean('') => false Boolean(undefined) => false Boolean(null) => false Boolean(0) => false
Number转String以前,首先会作一个去0和补0的操做,而后再去转成String类型。
String(1.234) => "1.234" String(NaN) => "NaN" String(.1234) => "0.1234" String(1.23400) => "1.234"
String(true) => "true" String(false) => "false"
String(null) => "null" String(undefined) => "undefined"
引用类型先ToPrimitive转换成原始数据类型,若转换后的原始数据类型不是String类型,再作String类型的转换。
const obj = { toString(){ console.log('调用了toString'); return true; } } console.log(String(obj)) 控制台输出结果: >>>>>>>>>>>>>>>>>> 调用了toString "true"
“==”下的类型转换,要分为两种状况来考虑。第一种,原始数据类型;第二种,引用类型。原始数据类型中String类型和Boolean类型是须要转换成Number类型再去比较的,而引用类型则须要先转换成原始数据类型再进行后续的转换。搞清整个流程以后,咱们再去理解“==”涉及到的ToNumber和ToPrimitive是如何进行转换的。按照这样子的理解步骤走,咱们对“==”隐藏下的类型转换会有比较清晰的认识。另外,“==”不涉及到ToString和ToBoolean的类型转换,在文章的后面部分我也加上去了,但愿能够给小伙伴们一个更加全面的类型转换的认识。最后附上完整的“==”类型转换的流程图: