寬松相等(loose equals) == 和嚴(yán)格相等(strict equals) === 都用來判斷兩個(gè)值是否“相等”莺匠,但是它們之間有一個(gè)很重要的區(qū)別纺荧,特別是在判斷條件上晴埂。
常見的誤區(qū)是“ == 檢查值是否相等痕囱, === 檢查值和類型是否相等”诞吱。聽起來蠻有道理剂陡,然而還不夠準(zhǔn)確。很多 JavaScript 的書籍和博客也是這樣來解釋的狐胎,但是很遺憾他們都錯(cuò)了鸭栖。
正確的解釋是:“ == 允許在相等比較中進(jìn)行強(qiáng)制類型轉(zhuǎn)換,而 === 不允許握巢≡稳担”
上面兩種解釋的區(qū)別:
- 第一種解釋(不準(zhǔn)確的版本), === 似乎比 == 做的事情更多暴浦,因?yàn)樗€要檢查值的類型溅话。
- 第二種解釋中 == 的工作量更大一些,因?yàn)槿绻档念愋筒煌€需要進(jìn)行強(qiáng)制類型轉(zhuǎn)換歌焦。
但是飞几,
- 雖然強(qiáng)制類型轉(zhuǎn)換確實(shí)要多花點(diǎn)時(shí)間,但僅僅是微秒級(jí)(百萬分之一秒)的差別而已独撇。
- == 和 === 都會(huì)檢查操作數(shù)的類型屑墨。真正的區(qū)別在于操作數(shù)類型不同時(shí)它們的處理方式不同。
拓展知識(shí)點(diǎn):
1. 存在 寬松不相等(loose not-equality) != 就是 == 的相反值纷铣, 嚴(yán)格不相等 !== 同理
2. 兩個(gè)值的類型相同時(shí)卵史,則僅比較它們是否相等,不發(fā)生強(qiáng)制類型轉(zhuǎn)換搜立。此時(shí)寬松相等與嚴(yán)格相等并無區(qū)別
3. 兩個(gè)值的類型不相同時(shí)以躯,寬松相等判斷會(huì)進(jìn)行類型轉(zhuǎn)換后 再進(jìn)行判斷比較。而轉(zhuǎn)換的類型順序總結(jié)下來是:
① 若存在一個(gè)為字符串類型啄踊,另一個(gè)可直接轉(zhuǎn)換為字符串類型 進(jìn)行比較忧设;
② 若存在一個(gè)為數(shù)字類型A,另一個(gè)為非數(shù)字類型B颠通,則非數(shù)字類型B 需轉(zhuǎn)換為數(shù)字類型后再比較址晕,即 ToNumber(B) == A。
4. 判斷使用建議:
var a = "42";
// 不要這樣用蒜哀,條件判斷不成立:
if (a == true) {
// ..
}
// 也不要這樣用斩箫,條件判斷不成立:
if (a === true) {
// ..
}
// 這樣的顯式用法沒問題:
if (a) {
// ..
}
// 這樣的顯式用法更好:
if (!!a) {
// ..
}
// 這樣的顯式用法也很好:
if (Boolean( a )) {
// ..
}
建議無論什么情況下都不要使用 == true 和 == false !!
5. 對(duì)象和非對(duì)象之間的相等比較
JS 的“拆封”:即“打開”封裝對(duì)象(如 new String("abc") ),返回其中的基本數(shù)據(jù)類型值( "abc" )撵儿。
而 == 中的 ToPromitive 強(qiáng)制類型轉(zhuǎn)換也會(huì)發(fā)生這樣的情況:先將對(duì)象類型的數(shù)據(jù) 調(diào)用 ToPromitive 抽象操作乘客,取出對(duì)象中 valueOf() 的返回值,再根據(jù)實(shí)際需要進(jìn)行強(qiáng)制類型轉(zhuǎn)換后 進(jìn)行比較淀歇。
e.g.
var a = "abc";
var b = Object( a ); // 和new String( a )一樣
a === b; // false
a == b; // true
a == b 結(jié)果為 true 易核,因?yàn)?b 通過 ToPromitive 進(jìn)行強(qiáng)制類型轉(zhuǎn)換(也稱為“拆封”,英文為 unboxed 或者 unwrapped)浪默,并返回標(biāo)量基本類型值 "abc" , 與 a 相等.
PS:
? NaN 不等于 NaN ( JS 數(shù)據(jù)類型中 唯一跟自身不相等的類型)
? +0 等于 -0
但是牡直,也有一些值不這樣,原因是 == 算法中其他優(yōu)先級(jí)更高的規(guī)則纳决。
e.g.:
var a = null;
var b = Object( a ); // 和Object()一樣
a == b; // false
var c = undefined;
var d = Object( c ); // 和Object()一樣
c == d; // false
var e = NaN;
var f = Object( e ); // 和new Number( e )一樣
e == f; // false
因?yàn)闆]有對(duì)應(yīng)的封裝對(duì)象碰逸,所以 null 和 undefined 不能夠被封裝(boxed), Object(null) 和 Object() 均返回一個(gè)常規(guī)對(duì)象阔加。NaN 能夠被封裝為數(shù)字封裝對(duì)象饵史,但拆封之后 NaN == NaN 返回 false ,因?yàn)?NaN 不等于 N
6. 寬松相等比較 需要特別注意的 情況
① null 和 undefined 之間的相等比較
需要注意到 在 == 中 null 和 undefined 相等(它們也與其自身相等)胜榔,除此之外其他值都不存在這種相等的情況 !!
e.g.
var a = null;
var b;
a == b; // true
a == null; // true
b == null; // true
a == false; // false
b == false; // false
a == ""; // false
b == ""; // false
a == 0; // false
b == 0; // false
② 下面羅列了比較特別的判斷情況胳喷,需要注意標(biāo)注 暈 的數(shù)據(jù)
"0" == null; // false
"0" == undefined; // false
"0" == false; // true -- 暈!
"0" == NaN; // false
"0" == 0; // true
"0" == ""; // false
false == null; // false
false == undefined; // false
false == NaN; // false
false == 0; // true -- 暈夭织!
false == ""; // true -- 暈吭露!
false == []; // true -- 暈!
false == {}; // false
"" == null; // false
"" == undefined; // false
"" == NaN; // false
"" == 0; // true -- 暈尊惰!
"" == []; // true -- 暈讲竿!
"" == {}; // false
0 == null; // false
0 == undefined; // false
0 == NaN; // false
0 == []; // true -- 暈!
0 == {}; // false
③
e.g.1: [] == ![] // true
上面例子里 弄屡!運(yùn)算符做的操作是:根據(jù) ToBoolean 規(guī)則戴卜,它會(huì)進(jìn)行布爾值的顯式強(qiáng)制類型轉(zhuǎn)換(同時(shí)反轉(zhuǎn)奇偶校驗(yàn)位)。所以 [] == ![] 變成了 [] == false 琢岩。前
面我們講過 false == [] 投剥,最后的結(jié)果就順理成章了.
e.g.2:
2 == [2]; // true
"" == [null]; // true
上面例子中,== 右邊的值 [2] 和 [null] 會(huì)進(jìn)行 ToPrimitive 強(qiáng)制類型轉(zhuǎn)換担孔,以便能夠和左邊的基本類型值( 2 和 "" )進(jìn)行比較江锨。因?yàn)閿?shù)組的 valueOf() 返回?cái)?shù)組本身,
所以強(qiáng)制類型轉(zhuǎn)換過程中數(shù)組會(huì)進(jìn)行字符串化糕篇。
第一行中的 [2] 會(huì)轉(zhuǎn)換為 "2" 啄育,然后通過 ToNumber 轉(zhuǎn)換為 2 。
第二行中的 [null] 會(huì)直接轉(zhuǎn)換為 "" 拌消。
所以最后的結(jié)果就是 2 == 2 和 "" == "" 挑豌。
e.g.3:
0 == "\n"; // true
"" 、 "\n" (或者 " " 等其他空格組合)等空字符串被 ToNumber 強(qiáng)制類型轉(zhuǎn)換為 0 ,故上面例子的比較結(jié)果為true.
④ 整理上面內(nèi)容氓英,
建議無論什么情況下都不要使用 == true 和 == false 做為判斷 !!
侯勉, 如下
"0" == false; // true -- 暈!
false == 0; // true -- 暈铝阐!
false == ""; // true -- 暈址貌!
false == []; // true -- 暈!
"" == 0; // true -- 暈徘键!
"" == []; // true -- 暈练对!
0 == []; // true -- 暈
寬松判斷時(shí),注意避免 值為 “ ”吹害、[]螟凭、0 的情況, 如下
"" == 0; // true -- 暈它呀!
"" == []; // true -- 暈赂摆!
0 == []; // true -- 暈!
日常寫的方法函數(shù)中钟些,需要注意
function doSomething(a) {
if (a == "") {
// ..
}
}
如果碰到 doSomething(0) 和 doSomething([]) 這樣的情況烟号,會(huì)造成函數(shù)內(nèi)判斷相等而導(dǎo)致報(bào)錯(cuò)。
故保持兩個(gè)原則:
? 如果兩邊的值中有 true 或者 false 政恍,千萬不要使用 == 汪拥。
? 如果兩邊的值中有 [] 、 "" 或者 0 篙耗,盡量不要使用 == 迫筑。
這時(shí)最好用 === 來避免不經(jīng)意的強(qiáng)制類型轉(zhuǎn)換。