JavaScript:什么時候能用==
答案是:永遠不要用.本文依次否定了五種看起來可以使用==的地方,同時解釋了為什么.
JavaScript中有兩個運算符用來判斷兩個值是否相等:
嚴格相等運算符===,必須類型相同且值相同.
普通的(寬容的)相等運算符==,在比較值是否嚴格相等之前,首先進行類型轉換.
給JavaScript初學者的建議是:完全忘掉==,總是使用===.實踐證明,這樣做是完全正確的.有五種案例看起來可以反駁這一論點,但其實沒有.從現在開始,我們的指導原則是:
比起簡潔,我們更喜歡意圖清晰的代碼.
記住:你的代碼只寫一次,但可能要讀很多次 – 請盡可能的讓你的代碼易讀.
案例1:你知道你在比較什么
例如,如果使用的是typeof操作符,你可以確定它返回來的是個字符串.那么使用==就是安全的,因為我們可以確定這樣的比較不會有任何的類型轉換:
- if (typeof x == "function") {
- ...
- }
可是,有兩個理由讓我們不去這樣做:
一致性: 如果你在這里使用了==,你不會得到任何的好處,那還何必破壞統一的規則去使用它?
簡單和性能: 通常來說, === 的操作更簡單,因為它不用去轉換它的操作數.在不同的JavaScript引擎上,該運算符的性能表現不同,但在大部分瀏覽器上,===總比==快,至少不會慢.
案例2: 比較undefined和null
使用==來比較的話,undefined和null是在一個相同的等價類上 – 他們和自己相等,同時互相之間也相等,但是不和其他任何值相等(包括那些JavaScript中的假值):
- > null == null
- true
- > undefined == null
- true
- > false == null
- false
- > 0 == null
- false
因此,下面的if語句可以檢測x是null或者是undefined(譯者注:一箭雙雕,jQuery1.81源碼中有36處地方使用了== null).
- if (x == null) {
- ...
- }
可是,代碼的簡潔性完全被代碼缺少明確的意圖這一缺點抵消掉了: 如果需要檢測undefined值,那你還不如把它也寫上. 否則,如果一個JavaScript新手讀你的代碼,他們會覺的你只是在檢測null.如果一個老手讀你的代碼,他們也許會認為也許是你犯了一個錯,這里應該用的是 ===.
- if (x === undefined || x === null) {
- ...
- }
如果你的需求不是那么的嚴格,那么上面的代碼可以簡寫為
- if (!x) {
- ...
- }
只要x為下面列出的這些假值,條件檢測就會通過.
- undefined
- null
- false
- 0
- ""
譯者注:5種原始值類型中,剛好各有一個假值?不對,少了個NaN
案例3: 比較字符串和數字
案例場景是:你正在寫和用戶界面交互的代碼或者處理服務器返回參數的代碼.那么你很可能需要處理字符串類型的數字.如果x是這樣一個字符串,你可以這樣比較它:
- if (x == 123) {
- ...
- }
但是為什么不告訴讀你代碼的人x不是一個數字呢,更好的寫法是明確的將它轉換成數字再進行比較.
- if (Number(x) === 123) {
- ...
- }
案例4: 比較對象值和原始值
使用 ==,你可以比較兩個原始值,也可以比較一個原始值和它的包裝類型對象值:
- > function isAbc(x) { return x == "abc" }
- > isAbc("abc")
- true
- > isAbc(new String("abc"))
- true
使用 === 的話,不行:
- > new String("abc") === "abc"
- false
等號左邊是一個對象值,等號右邊是一個原始值.因此,他們沒有相同的類型,也就不能嚴格相等.盡管如此,你仍然應該優先考慮,如何才能讓讀你代碼的人清楚的知道你這行代碼的意圖.如果用下面這個判斷的話
- x == "abc"
你想要完成的任務是什么?
左側的值真的既可能是一個字符串包裝對象又可能是一個字符串原始值嗎?這似乎不太可能,但如果真是這樣的話,你應該在文檔中很清楚的寫明這到底是個什么樣的操作.
你是想把對象值轉換成原始值嗎? 那么你可以寫的更明確一點
- String(x) === "abc"
你是想要拆箱出一個原始值嗎?那么你應該這么寫
- x.valueOf() === "abc"
案例5: JavaScript是很靈活的 – 我寫的代碼也應該如此
這個論點是這樣的:我希望我的代碼能表現出和JavaScript同樣的靈活性.并且 == 能幫我實現它.下面是體現JavaScript靈活性的例子,它能自動轉換值的類型:
- > "abc" + false
- 'abcfalse'
- > 3 + true
- 4
- > +"73"
- 73
下面是上述論點的幾個反駁:
標準的隱式轉換并不可能總是符合你的期望.例如:
- > !"false"
- false
- > 7 + "3"
- '73'
- > Number("")
- 0
寬容的相等判斷和通常見到的隱式轉換有著不同的表現:
- > 2 == false
- false
- > 2 == true
- false
- > Boolean(2)
- true
明確的類型轉化加上嚴格的相等判斷能夠產生更具描述性的代碼.不好的寫法:用寬容相等來實現靈活性
- function is123Implicit(x) {
- return x == 123;
- }
- > is123Implicit(123)
- true
- > is123Implicit(new Number(123))
- true
- > is123Implicit("123")
- true
替代方法: 明確的類型轉換加嚴格相等實現靈活性:
- function is123Explicit(x) {
- x = Number(x);
- return x === 123;
- }
- > is123Explicit(123)
- true
- > is123Explicit(new Number(123))
- true
- > is123Explicit("123")
- true
誰說你的代碼必須要靈活?可以說JavaScript這種默認的靈活性是一個bug而不是特性.編寫防御型的代碼能更快的暴露出這些bug.一個更有防御性的函數is123Explicit()如下:
- function is123Defensive(x) {
- if (typeof x !== "number") {
- throw new TypeError("Not a number: "+x);
- }
- return x === 123;
- }
如果你想傳入一個非數字原始值的參數,那么你得首先進行類型轉換.
結論
我希望我已經說服了你,應該堅持以淺顯易懂為原則,堅決不使用==來做判斷.這樣做是有道理的,不光針對新手,通常來說,代碼中有更少技巧性的東西,就意味著代碼有更好的可讀性。
原文鏈接:http://www.cnblogs.com/ziyunfei/archive/2012/09/22/2696109.html