這一節(jié),應(yīng)該算是強(qiáng)制類(lèi)型轉(zhuǎn)換的最后一個(gè)小節(jié)了呼渣,這一部分呢擂橘,主要會(huì)講比較操作中遇到的強(qiáng)制類(lèi)型轉(zhuǎn)換晌区。
抽象相等(==)和嚴(yán)格相等(===)。
簡(jiǎn)單且粗略的來(lái)說(shuō)通贞,抽象相等和嚴(yán)格相等的區(qū)別就是抽象相等在比較的時(shí)候朗若,如果比較的兩個(gè)數(shù)類(lèi)型不同,會(huì)先進(jìn)行類(lèi)型轉(zhuǎn)換再比較昌罩,而嚴(yán)格類(lèi)型呢哭懈,比較簡(jiǎn)單粗暴一些,直接返回false茎用。
當(dāng)然啦遣总,你也可以這么理解你虹,抽象比較的時(shí)候,允許類(lèi)型轉(zhuǎn)換彤避,而嚴(yán)格相等則不允許,所以看如下例子:
console.log('1111' == 1111)? // true
console.log('1111' === 1111)? // false
這個(gè)例子很容易理解夯辖,那么本例中琉预,抽象相等中究竟是從字符串轉(zhuǎn)換為數(shù)字呢,還是相反蒿褂?
規(guī)范是這么說(shuō)的:
如果相比較的兩個(gè)操作數(shù)圆米,其中一個(gè)是數(shù)字類(lèi)型,另一個(gè)是字符串類(lèi)型的話啄栓,那么字符串將會(huì)轉(zhuǎn)換為數(shù)字娄帖,再進(jìn)行比較,就相等于:
console.log(Number('1111') == 1111);
那么如果是布爾值呢昙楚?比如說(shuō)
console.log("42" == true);? // false
console.log(12 == true);? // false
console.log(-1 == true);? // false
哇哦近速,都是false呀,是不是和一開(kāi)始的認(rèn)知不太一樣呢堪旧?尤其是對(duì)于有其他語(yǔ)言基礎(chǔ)的童鞋們削葱。
這一方面ecma規(guī)范也有說(shuō)了:
如果操作數(shù)中,有布爾類(lèi)型的淳梦,那么他將會(huì)轉(zhuǎn)為數(shù)字類(lèi)型析砸,再進(jìn)行比較。
大家請(qǐng)看著個(gè)例子爆袍,應(yīng)該不用多說(shuō)了吧首繁,上面說(shuō)過(guò)了,嚴(yán)格相等的話陨囊,如果類(lèi)型不一樣弦疮,直接返回false,畢竟人家是嚴(yán)格相等谆扎,很?chē)?yán)格的挂捅。
console.log(false === 0); // false
console.log(false == 0); // true
那么如果null和undefined比較呢?
console.log(null == null); // true
console.log(undefined == null); // true
console.log(undefined == undefined) // true
第一個(gè)和第三個(gè)大家比較容易理解堂湖,第二個(gè)可能比較疑惑闲先,為甚呢?
因?yàn)橐?guī)范上有說(shuō)无蜂,如果比較的兩種伺糠,一個(gè)是undefined另一種是null,則返回true斥季,但是這個(gè)也只是對(duì)應(yīng)于抽象相等训桶,嚴(yán)格相等時(shí)不可能相等的累驮。因?yàn)轭?lèi)型不一樣。
至于上面那個(gè)呢舵揭,我還應(yīng)該多說(shuō)一句谤专,除了undefined和null比較或者是他們同類(lèi)型的比較是true,和其他任何類(lèi)型的值比較都是false午绳,有一些看起來(lái)像true的置侍,結(jié)果都是false,要注意一下拦焚。
console.log(null == false); // false
console.log(undefined == 0); // false
console.log(undefined == "") // false
接下來(lái)這個(gè)比較重要了蜡坊,就是對(duì)象和非對(duì)象的比較。
先看規(guī)范定義吧:
對(duì)于兩個(gè)操作數(shù)赎败,如果其中一個(gè)是字符串或數(shù)字秕衙,另一個(gè)是對(duì)象的話,那么對(duì)象會(huì)轉(zhuǎn)為原始值僵刮,然后再進(jìn)行比較据忘。
那么怎么獲取原始值呢?
其實(shí)其他小節(jié)也都講過(guò)搞糕,這里在復(fù)述一下若河,簡(jiǎn)單來(lái)說(shuō),就是先調(diào)用對(duì)象的valueOf()函數(shù)寞宫,如果它不存在萧福,或者不會(huì)轉(zhuǎn)為基本類(lèi)型值,就調(diào)用toString()函數(shù)辈赋,如果toString()不存在或者返回的是非字符串的值鲫忍,將會(huì)直接報(bào)錯(cuò)。
看起來(lái)有一點(diǎn)枯燥吧钥屈,那么看例子悟民。
console.log([2].valueOf());? // [2]
console.log([2].toString());? // "2"
console.log([2].toString() == 2);? // true
數(shù)組[2]呢,可以看到篷就,他的valueOf返回的是一個(gè)數(shù)組射亏,那么他就會(huì)用toString(),轉(zhuǎn)為字符串“2”竭业,字符串2和數(shù)字2比較呢智润,根據(jù)上面講的,字符串2會(huì)變?yōu)閿?shù)字2未辆,相等窟绷,返回true。
在看一個(gè)例子
var obj = {
valueOf() {
return 3;
}
}
console.log(obj == 3);? // true
var obj1 = Object.create(null);
console.log(obj1 == 3);? // Uncaught TypeError: Cannot convert object to primitive value
這個(gè)呢咐柜,就是對(duì)象先調(diào)用valueOf()得到基本類(lèi)型值3兼蜈,然后再進(jìn)行比較得到true攘残,第二個(gè)呢,得到了一個(gè)純凈的對(duì)象(沒(méi)有prototype),然后獲取不到valueOf()和toString()为狸,直接報(bào)錯(cuò)了歼郭。
那其他的情況呢
console.log(NaN == NaN);? // false
console.log(NaN === NaN);? // false
console.log(+0 == -0);? // true
console.log(+0 === -0);? // true
console.log({} == {});? // false
console.log({} === {});? // false
var obj1 = obj = {};
console.log(obj == obj1);? // true
console.log(obj === obj1);? // true
這個(gè)分析一下,規(guī)范中:
NaN不等于自身辐棒,+0和-0是相等的实撒,對(duì)象是否相等是根據(jù)是否引用同一對(duì)象。
對(duì)象相等的已經(jīng)介紹完了涉瘾,那么判斷不相等的呢?比如說(shuō)(!=)和(!==)他們的區(qū)別呢
實(shí)際上捷兰,他們的語(yǔ)法判斷規(guī)則和相等的規(guī)則一樣立叛,只不過(guò)最后多了一個(gè)置反的一個(gè)步驟。也就是!(a == b)或者是!(a === b)贡茅,我感覺(jué)應(yīng)該很容易理解秘蛇,就不舉例了。
那么最后就要講關(guān)系操作符了顶考,也就是大于小于這些的赁还。
那么我們先講兩個(gè)操作符中,有至少是一個(gè)數(shù)字的情況驹沿。請(qǐng)看下面的例子
console.log(1 < 2);? // true
console.log("0b1" < 2);? // true
var obj = {
valueOf() {
return 1;
}
}
console.log(obj < 2);? // true
console.log(1 < Infinity);? // true
console.log(-Infinity < 1);? // true
console.log(NaN > 1);? // false
console.log(NaN < 1);? // false
上面這幾個(gè)例子艘策,幾乎涵蓋了規(guī)范中至少有一個(gè)操作數(shù)是數(shù)字的比較的情況。
首先渊季,也是如果有對(duì)象的話朋蔫,會(huì)把對(duì)象轉(zhuǎn)為基本類(lèi)型值,在進(jìn)行比較却汉。
并且如果另一個(gè)操作數(shù)是字符串的話驯妄,會(huì)把字符串轉(zhuǎn)成數(shù)字。
還有就是數(shù)字一直小于正無(wú)窮合砂,大于負(fù)無(wú)窮青扔。
NaN無(wú)論怎么判斷都是false。
那么如果是字符串之間的比較呢翩伪,也就是倆操作數(shù)都是字符串的情況微猖。
console.log("1003" > "2");? // false
嗯,很簡(jiǎn)單是吧缘屹,如果倆都是字符串的話励两,實(shí)際上會(huì)按照字母順序去比,這樣去排出哪個(gè)值囊颅。
那么是怎么按照字母順序比的呢当悔,字母順序又是通過(guò)什么方式取得的呢傅瞻?請(qǐng)看例子:
console.log("aaa" > "aa"); // true
console.log("1003" > "2");? // false
console.log("a" > "b");? // false
console.log('&' < "a");? // true
console.log("A" < "a");? // true
console.log("a".charCodeAt());? // 97
console.log("b".charCodeAt());? // 98
console.log("&".charCodeAt());? // 38
console.log("A".charCodeAt());? // 65
這個(gè)例子,應(yīng)該就很容易理解了盲憎,實(shí)際上取的是字符串的charCodeAt()嗅骄,實(shí)際上你依然可以理解為轉(zhuǎn)換成數(shù)字去比了,只不過(guò)字符串的比和含有數(shù)字的比是不一樣的饼疙,數(shù)字的比意味著他們整體數(shù)字的值的大小去比溺森,而字符串是比從一開(kāi)始的前綴挨個(gè)比每個(gè)字母的大小。
是不是依然比較好理解窑眯,那么在來(lái)一個(gè)例子屏积。
var a = [ 42 ];
var b = "043";
console.log(a < b);? // false
console.log(Number(a) < Number(b)); // true
這個(gè)結(jié)合上面那個(gè)規(guī)則,自己分析下磅甩。
最后的最后炊林,我們看一個(gè)你可能略感疑惑的例子:
var a = { b: 42 };
var b = { b: 43 };
console.log(a < b);? // false
console.log(a == b); // false
console.log(a > b);? // false
console.log(a <= b);? // true
console.log(a >= b);? // true
這個(gè)例子,確實(shí)不太付合常識(shí)呀卷要,但是呢渣聚,這確實(shí)是存在的,我給解釋一下僧叉。
首先呢奕枝,a < b和a > b,由于他們倆都是對(duì)象瓶堕,所以轉(zhuǎn)換為基本類(lèi)型值后為字符串“[object Object]”隘道,所以返回false,而a == b呢郎笆,則是因?yàn)樗麄z并不是同一個(gè)對(duì)象的不同的引用薄声,所以返回false。
最后倆呢题画,實(shí)際上大于等于或者小于等于可以理解為大于或者小于的值的反值默辨,也就是: !(a <= b),所以前面為false苍息,反一下為true
好啦缩幸,三個(gè)小章基本把強(qiáng)制類(lèi)型轉(zhuǎn)換整個(gè)梳理了一遍,因?yàn)檫@一塊的細(xì)節(jié)太多竞思,所以說(shuō)呢表谊,我反倒是建議讀到我這里的小伙伴,先把大體規(guī)則了解以后盖喷,特別細(xì)節(jié)的規(guī)則爆办,用到的時(shí)候看看我的小散文,或者直接去看ecma262標(biāo)準(zhǔn)文檔也行课梳,但是呢距辆,強(qiáng)調(diào)一點(diǎn)就是余佃,假如真的要用之前,還是建議自己吧代碼跑一下跨算,這樣我的感觸就是要比只看效果好太多爆土。
總之感謝大家收看我的小散文。
參考書(shū)籍《你不知道的Javascript中卷》
參考文檔:ECMAScript 5.1(ECMA-262)
https://www.ecma-international.org/ecma-262/5.1/
本文轉(zhuǎn)載自http://www.lht.ren/article/7/