Javascript基礎之-強制型別轉換(三)

小辭發表於2019-02-16

這一節,應該算是強制型別轉換的最後一個小節了,這一部分呢,主要會講比較操作中遇到的強制型別轉換。

抽象相等(==)和嚴格相等(===)。

簡單且粗略的來說,抽象相等和嚴格相等的區別就是抽象相等在比較的時候,如果比較的兩個數型別不同,會先進行型別轉換再比較,而嚴格型別呢,比較簡單粗暴一些,直接返回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)

https://www.ecma-internationa…

本文轉載自http://www.lht.ren/article/7/

相關文章