Truth, Equality and JavaScript(譯)

【原文地址】https://javascriptweblog.wordpress.com/2011/02/07/truth-equality-and-javascript/
一篇 2011 年的老文章驳规,很贊同作者說的 you can’t master a language until you know it inside out – and fear and evasion are the enemies of knowledge. 閱讀之焦除,學習之闻坚。

先看幾個老鳥也會覺得很 confuse 的例子

if( [ 0 ] ) {
    console.log( [0] == true );     // false
    console.log( !![0] );           // true
}

if( 'potato' ) {
    console.log( 'potato' == false );   // false
    console.log( 'potato' == true );    // false
}

上面的例子看來使用 JavaScript 進行判斷的時候結果常常是出乎意料的世舰。有個好消息是已經(jīng)有標準文檔規(guī)定這些表達式應該返回怎樣的值汗茄,并且所有的瀏覽器都將遵循該規(guī)則進行實現(xiàn)答倡。

JavaScript 中關于 truthequality 的求值主要有以下三種情況:

  • conditional statements and operators( 條件申明和操作: if, ? :, ||, && etc )
  • the equal operator( 等于操作符: == )
  • the strict equals operator( 嚴格等于操作符: === )

Conditional 條件聲明和操作

在 JavaScript 中董济,所有的條件申明操作遵循相同的取值規(guī)則踱阿。下面將以 if 為例解釋這個規(guī)則泽疆。
if 聲明(表達式)會使用 ES5 中定義的一個抽象方法 ToBoolean 將其聲明(表達式) 轉換為一個布爾類型的值户矢。(ecma-262 (6,7th edition 在 7.1.2, 5th edition 在 9.2 ))[http://www.ecma-international.org/ecma-262/7.0/index.html#sec-toboolean]

| 參數(shù)(表達式)類型 | 結果 |
|: --- :|: --- :|
| Undefined | false |
| Null | false |
| Boolean | 傳入?yún)?shù)值(不做類型轉換)|
| Number | +0, -0 和 NaN 會被當做是 false,其余值都是 true |
| String | 空字符串(長度為0) 會被當做是 false, 其余值都是 true |
| Object | 所有對象都會被當做是 true |

這是 JavaScript 所使用的判斷一個值是 truthy (eg: true, 'potato', 36, [12, 34]) 或 falsey (eg: false, 0, '') 的規(guī)則殉疼。

看到這里就能明白為什么 if([0]) 會允許進入后面的代碼塊執(zhí)行了 (數(shù)組是一個對象梯浪,對象都被按照 true 解析)捌年。

再來看一些或許會讓你覺得奇怪,但是卻是符合上面描述的規(guī)則的例子:

var trutheyTester = function(expr) {
    return expr ? 'truthey' : 'falsey';
}

trutheyTester({});  // truthey( 對象都被當做true )

trutheyTester(false);   // falsey
trutheyTester(new Boolean(false));  // truthey (new Boolean(false) 返回的是對象)

trutheyTester('');  // falsey
trutheyTester(new String(''));  // truthey

trutheyTester(NaN); // falsey
trutheyTester(new Number(NaN));     // truthey

The Equals Operator(==) 等于操作符

== 型的等于操作符(以下稱為非嚴格等于)是非常自由的(liberal)挂洛。兩邊的值有可能根本就會是不同的類型礼预,但運算卻有可能返回 true 的結果。因為非嚴格等于操作會對將要進行比較運算的一邊或者兩邊進行強制類型轉換再進行比較(通常都是裝換為number型的值)虏劲。

很明顯使用 == 操作符比較不同類型的值逆瑞,且可能返回 true 的結果,很酷伙单,但同時也很危險获高。我們的某一位 JavaScript 領域專家建議完全不要使用 == 運算符。

這種避免使用 == 的建議我并不是十分贊同吻育,因為我認為只有深入的學習這門語言你才能算是掌握(master)了它念秧。回避是真正掌握知識的敵人布疼。

而且摊趾,即使我們假裝 == 操作符并不存在,你也依然無法擺脫 JavaScript 中大量存在的類型裝換問題游两。如果能夠恰當?shù)厥褂?== 進行比較砾层,將會是一個很好的工具,用來創(chuàng)建出更簡潔贱案,優(yōu)雅和可讀的代碼肛炮。

無論如何,讓我們來看看 ECMA 是如何定義 == 運算符的標準表現(xiàn)的宝踪。它實際上并沒有想象中那么嚇人侨糟,只需要記住使用 == 比較的時候 undefined 和 null 是相等的,大多數(shù)情況下其他類型的參數(shù)會被轉換成數(shù)字類型用來幫助進行更好的比較瘩燥。

| type(x) | type(y) | result |
|: --- :|: --- :|: --- :|
| null | undefined | true |
| undefined | null | true |
| Number | String | x == toNumber(Y) |
| String | Number | toNumber(x) == y |
| Boolean | (any) | toNumber(x) == y |
| (any) | Boolean | x == toNumber(y) |
| String or Number | Object | x == toPrimitive(y) |
| Object | String or Number | toPrimitive(x) == y |
| (any) | (any) 和 x 類型相同 | 參考 x === y |
| 其他一些情況 | ... | false |

只要得到的結果還是一個表達式秕重,該算法就會重新執(zhí)行,直到最終返回布爾型的結果為止厉膀。
toNumbertoPrimitive 是兩個內置方法溶耘,按照下面的規(guī)則對傳入的參數(shù)進行轉換。

toNumber

| Argument Type | Result |
|: --- :|: --- :|
| Undefined | NaN |
| Null | +0 |
| Boolean | true -> 1, false -> +0 |
| Number | 傳入?yún)?shù)服鹅,不做轉換 |
| String | 'abc' -> NaN, '123' -> 123 |
| Object | 先使用 toPrimitive 裝換對象值凳兵,然后在對 toPrimitive 得到的值進行,toNumber 運算|

toPrimitive

| Argument Type | Result |
|: --- :|: --- :|
| Object | 在 比較運算中菱魔,如果 valueOf 方法返回一個原始類型值留荔,返回這個值。如果 toString 方法返回 原始類型值澜倦,返回這個值聚蝶。如果前面兩個方法都不返回原始類型值,拋出一個錯誤 |
| otherwise ... | 不進行類型轉換藻治,直接返回傳入的參數(shù) |

學習了上面的規(guī)則碘勉,來看幾個實際的例子:
[0] == true;

[0] == true;    // false

// 偽碼
// 1. 布爾型 使用 toNumber 轉換
[0] == 1;
// 2. 對象使用 valueOf 或 toString 獲得原始類型值
'0' == 1;
// 3. string 使用 toNumber 裝換
0 == 1; // false

'potato' == true;

'potato' == true;   // false

// 偽碼
// 1. 布爾型 使用 toNumber 轉換
'potato' == 1;
// 2. string 使用 toNumber 轉換
NaN == 1;   // false

'potato' == false;

'potato' == false;  // false

// 偽碼
// 1. 布爾型 使用 toNumber 轉換
'potato' == 0;
// 2. string 使用 toNumber 轉換

Object with valueOf

crazyNumeric = new Number(1);
crazyNumeric.toString = function() { return '2' };
crazyNumeric == 1; // true

// 偽碼
// 1. 對象 使用 valueOf 或 toString 轉換
// 1 == 1;  // true

Object with toString

crazyNumeric = {
    toString: function() { return '2' };
}
crazyNumeric == 1;

// 偽碼
// 1. 對象 使用 valueOf 或 toString 轉換
// 2 == 1;  // false

兩邊類型裝換總是按照這樣的順序進行 Boolean > Object( valurOf > toString ) > String(譯者參考后面提供的 javascript == 操作符比較流程工具 添加的內容)

The Strict Equals Operator( === ) 嚴格等于

這種情形會比較簡單。如果是對不同類型進行比較桩卵,結果永遠都會是 false验靡。
如果比較兩邊是同一數(shù)據(jù)類型的將使用一個直觀的規(guī)則進行比較: 對象比較,必須兩邊都是同一對象的引用時才會得到 true 的結果; 字符串必須包含完全相同的字符集(contain identical character sets)才會得到 true 的結果; 其他的原始類型必須值相同才能返回 true 的結果雏节。
NaN, null, undefined 三者都不會存在一個嚴格相等的組合胜嗓。 NaN 甚至都不等于它自身(NaN 是 JavaScript 中唯一不等于自身的值)。

一些本不需要使用 === 的場景

// unnecessary
if ( typeof myVar === 'function' ) { /* ... */ }

// better
if ( typeof myVar == 'function' ) { /* ... */ }

typeof 表達式返回一個 string 類型的值钩乍,這個操作永遠都是比較兩個字符串辞州。因此 == 在這種情況下永遠都能得到正確的比較結果。

// unnecessary 
var missing = ( myVar === undefined || myVar === null );

// better
var missing = ( myVar == null );

==undefinednull 彼此相等寥粹。
在某些 ES 實現(xiàn)中变过,undefined 是可以修改的, 因此和 null 進行比較更為安全。

// unnecessary 
if ( myArr.length === 3 ) { /* ... */ }

// better

if ( myArr.length == 3 ) { /* ...*/ }

都是數(shù)字的比較涝涤,== 在這種情況下永遠都能得到正確的比較結果媚狰。

擴展閱讀:

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末崭孤,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子糊肠,更是在濱河造成了極大的恐慌裳瘪,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,743評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件罪针,死亡現(xiàn)場離奇詭異彭羹,居然都是意外死亡,警方通過查閱死者的電腦和手機泪酱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評論 3 385
  • 文/潘曉璐 我一進店門派殷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人墓阀,你說我怎么就攤上這事毡惜。” “怎么了斯撮?”我有些...
    開封第一講書人閱讀 157,285評論 0 348
  • 文/不壞的土叔 我叫張陵经伙,是天一觀的道長。 經(jīng)常有香客問我,道長帕膜,這世上最難降的妖魔是什么枣氧? 我笑而不...
    開封第一講書人閱讀 56,485評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮垮刹,結果婚禮上达吞,老公的妹妹穿的比我還像新娘。我一直安慰自己荒典,他們只是感情好酪劫,可當我...
    茶點故事閱讀 65,581評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著寺董,像睡著了一般覆糟。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上遮咖,一...
    開封第一講書人閱讀 49,821評論 1 290
  • 那天滩字,我揣著相機與錄音,去河邊找鬼盯滚。 笑死踢械,一個胖子當著我的面吹牛,可吹牛的內容都是我干的魄藕。 我是一名探鬼主播内列,決...
    沈念sama閱讀 38,960評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼背率!你這毒婦竟也來了话瞧?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,719評論 0 266
  • 序言:老撾萬榮一對情侶失蹤寝姿,失蹤者是張志新(化名)和其女友劉穎交排,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體饵筑,經(jīng)...
    沈念sama閱讀 44,186評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡埃篓,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,516評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了根资。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片架专。...
    茶點故事閱讀 38,650評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖玄帕,靈堂內的尸體忽然破棺而出部脚,到底是詐尸還是另有隱情,我是刑警寧澤裤纹,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布委刘,位于F島的核電站,受9級特大地震影響,放射性物質發(fā)生泄漏锡移。R本人自食惡果不足惜呕童,卻給世界環(huán)境...
    茶點故事閱讀 39,936評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望罩抗。 院中可真熱鬧拉庵,春花似錦灿椅、人聲如沸套蒂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽操刀。三九已至,卻和暖如春婴洼,著一層夾襖步出監(jiān)牢的瞬間骨坑,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評論 1 266
  • 我被黑心中介騙來泰國打工柬采, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留欢唾,地道東北人。 一個月前我還...
    沈念sama閱讀 46,370評論 2 360
  • 正文 我出身青樓粉捻,卻偏偏與公主長得像礁遣,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子肩刃,可洞房花燭夜當晚...
    茶點故事閱讀 43,527評論 2 349

推薦閱讀更多精彩內容

  • 第5章 引用類型(返回首頁) 本章內容 使用對象 創(chuàng)建并操作數(shù)組 理解基本的JavaScript類型 使用基本類型...
    大學一百閱讀 3,216評論 0 4
  • 第1章 JavaScript 簡介 JavaScript 具備與瀏覽器窗口及其內容等幾乎所有方面交互的能力祟霍。 歐洲...
    力氣強閱讀 1,118評論 0 0
  • 原文: https://github.com/ecomfe/spec/blob/master/javascript...
    zock閱讀 3,371評論 2 36
  • FreeCodeCamp - Basic JavaScript 寫在前面: 我曾經(jīng)在進谷前刷過這一套題,不過當時只...
    付林恒閱讀 16,432評論 5 28
  • "沒有了盈包,沒有了沸呐,只剩下最後的五百塊!"我心里暗自著急。 這個月花錢如流水呢燥,買了手袋買了衣服還買了喜歡的化妝品崭添,因...
    安親閱讀 1,342評論 0 0