這一節,應該算是強制型別轉換的最後一個小節了,這一部分呢,主要會講比較操作中遇到的強制型別轉換。
抽象相等(==)和嚴格相等(===)。
簡單且粗略的來說,抽象相等和嚴格相等的區別就是抽象相等在比較的時候,如果比較的兩個數型別不同,會先進行型別轉換再比較,而嚴格型別呢,比較簡單粗暴一些,直接返回false。
當然啦,你也可以這麼理解,抽象比較的時候,允許型別轉換,而嚴格相等則不允許,所以看如下例子:
console.log(`1111` == 1111) // true
console.log(`1111` === 1111) // false
這個例子很容易理解,那麼本例中,抽象相等中究竟是從字串轉換為數字呢,還是相反?
規範是這麼說的:
如果相比較的兩個運算元,其中一個是數字型別,另一個是字串型別的話,那麼字串將會轉換為數字,再進行比較,就相等於:
console.log(Number(`1111`) == 1111);
那麼如果是布林值呢?比如說
console.log("42" == true); // false
console.log(12 == true); // false
console.log(-1 == true); // false
哇哦,都是false呀,是不是和一開始的認知不太一樣呢?尤其是對於有其他語言基礎的童鞋們。
這一方面ecma規範也有說了:
如果運算元中,有布林型別的,那麼他將會轉為數字型別,再進行比較。
大家請看著個例子,應該不用多說了吧,上面說過了,嚴格相等的話,如果型別不一樣,直接返回false,畢竟人家是嚴格相等,很嚴格的。
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
第一個和第三個大家比較容易理解,第二個可能比較疑惑,為甚呢?
因為規範上有說,如果比較的兩種,一個是undefined另一種是null,則返回true,但是這個也只是對應於抽象相等,嚴格相等時不可能相等的。因為型別不一樣。
至於上面那個呢,我還應該多說一句,除了undefined和null比較或者是他們同型別的比較是true,和其他任何型別的值比較都是false,有一些看起來像true的,結果都是false,要注意一下。
console.log(null == false); // false
console.log(undefined == 0); // false
console.log(undefined == "") // false
接下來這個比較重要了,就是物件和非物件的比較。
先看規範定義吧:
對於兩個運算元,如果其中一個是字串或數字,另一個是物件的話,那麼物件會轉為原始值,然後再進行比較。
那麼怎麼獲取原始值呢?
其實其他小節也都講過,這裡在複述一下,簡單來說,就是先呼叫物件的valueOf()函式,如果它不存在,或者不會轉為基本型別值,就呼叫toString()函式,如果toString()不存在或者返回的是非字串的值,將會直接報錯。
看起來有一點枯燥吧,那麼看例子。
console.log([2].valueOf()); // [2]
console.log([2].toString()); // "2"
console.log([2].toString() == 2); // true
陣列[2]呢,可以看到,他的valueOf返回的是一個陣列,那麼他就會用toString(),轉為字串“2”,字串2和數字2比較呢,根據上面講的,字串2會變為數字2,相等,返回true。
在看一個例子
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
這個呢,就是物件先呼叫valueOf()得到基本型別值3,然後再進行比較得到true,第二個呢,得到了一個純淨的物件(沒有prototype),然後獲取不到valueOf()和toString(),直接報錯了。
那其他的情況呢
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
這個分析一下,規範中:
NaN不等於自身,+0和-0是相等的,物件是否相等是根據是否引用同一物件。
物件相等的已經介紹完了,那麼判斷不相等的呢?比如說(!=)和(!==)他們的區別呢
實際上,他們的語法判斷規則和相等的規則一樣,只不過最後多了一個置反的一個步驟。也就是!(a == b)或者是!(a === b),我感覺應該很容易理解,就不舉例了。
那麼最後就要講關係操作符了,也就是大於小於這些的。
那麼我們先講兩個操作符中,有至少是一個數字的情況。請看下面的例子
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
上面這幾個例子,幾乎涵蓋了規範中至少有一個運算元是數字的比較的情況。
首先,也是如果有物件的話,會把物件轉為基本型別值,在進行比較。
並且如果另一個運算元是字串的話,會把字串轉成數字。
還有就是數字一直小於正無窮,大於負無窮。
NaN無論怎麼判斷都是false。
那麼如果是字串之間的比較呢,也就是倆運算元都是字串的情況。
console.log("1003" > "2"); // false
嗯,很簡單是吧,如果倆都是字串的話,實際上會按照字母順序去比,這樣去排出哪個值。
那麼是怎麼按照字母順序比的呢,字母順序又是通過什麼方式取得的呢?請看例子:
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
這個例子,應該就很容易理解了,實際上取的是字串的charCodeAt(),實際上你依然可以理解為轉換成數字去比了,只不過字串的比和含有數字的比是不一樣的,數字的比意味著他們整體數字的值的大小去比,而字串是比從一開始的字首挨個比每個字母的大小。
是不是依然比較好理解,那麼在來一個例子。
var a = [ 42 ];
var b = "043";
console.log(a < b); // false
console.log(Number(a) < Number(b)); // true
這個結合上面那個規則,自己分析下。
最後的最後,我們看一個你可能略感疑惑的例子:
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
這個例子,確實不太付合常識呀,但是呢,這確實是存在的,我給解釋一下。
首先呢,a < b和a > b,由於他們倆都是物件,所以轉換為基本型別值後為字串“[object Object]”,所以返回false,而a == b呢,則是因為他倆並不是同一個物件的不同的引用,所以返回false。
最後倆呢,實際上大於等於或者小於等於可以理解為大於或者小於的值的反值,也就是: !(a <= b),所以前面為false,反一下為true
好啦,三個小章基本把強制型別轉換整個梳理了一遍,因為這一塊的細節太多,所以說呢,我反倒是建議讀到我這裡的小夥伴,先把大體規則瞭解以後,特別細節的規則,用到的時候看看我的小散文,或者直接去看ecma262標準文件也行,但是呢,強調一點就是,假如真的要用之前,還是建議自己吧程式碼跑一下,這樣我的感觸就是要比只看效果好太多。
總之感謝大家收看我的小散文。
參考書籍《你不知道的Javascript中卷》
參考文件:ECMAScript 5.1(ECMA-262)