JavaScript 提供三種不同的比較操作符:
- 嚴格相等,使用 ===
- (非嚴格)相等,使用 ==
- 以及
(ECMAScript 6 新特性)Object.is
ES2015标準提供以下四種比較操作符:
- (非嚴格)相等 (
)==
- 嚴格相等 (
): 用于===
,Array.prototype.indexOf
, 以及Array.prototype.lastIndexOf
語句的比對操作case
- 零值相等: 用于
和TypedArray
的構造、ArrayBuffer
和Map
操作, 并将用于ES2016标準中的Set
String.prototype.includes
- 同值相等: 用于所有其他場景
你可以根據你的需要選擇操作符。
簡單地說,兩等号判等會在比較時進行類型轉換;三等号判等不會進行類型轉換(如果類型不同會直接傳回 false );
Object.is
在三等号判等的基礎上特别處理了
NaN
、
-0
和
+0
,保證 -0 和 +0 不再相同,但
Object.is(NaN, NaN)
會傳回
true
。(像其他數值一樣比較 NaN ——由于 IEEE 754 的規範,無論使用雙等号或三等号,比較 NaN 都會得到 false )但請注意,此外,這三個運算符的原語中,沒有一個會比較兩個變量是否結構上概念類似。對于任意兩個不同的非原始對象,即便他們有相同的結構, 以上三個運算符都會計算得到 false 。
嚴格相等 ===
EDIT
===
全等操作符比較兩個值是否相等,兩個被比較的值在比較前都不進行隐式轉換。如果兩個被比較的值具有不同的類型,這兩個值是不全等的。否則,如果兩個被比較的值類型相同,值也相同,并且都不是 number 類型時,兩個值全等。最後,如果兩個值都是 number 類型,當兩個都不是 NaN,并且數值相同,或是兩個值分别為 +0 和 -0 時,兩個值被認為是全等的。
var num = 0;
var obj = new String("0");
var str = "0";
var b = false;
console.log(num === num); // true
console.log(obj === obj); // true
console.log(str === str); // true
console.log(num === obj); // false
console.log(num === str); // false
console.log(obj === str); // false
console.log(null === undefined); // false
console.log(obj === null); // false
console.log(obj === undefined); // false
在日常中使用全等操作符幾乎總是正确的選擇。對于除了數值之外的值,全等操作符使用明确的語義進行比較:一個值隻與自身全等。對于數值,全等操作符使用略加修改的語義來處理兩個特殊情況:第一個情況是,浮點數 0 是不分正負的。區分 +0 和 -0 在解決一些特定的數學問題時是必要的,但是大部分境況下我們并不用關心。全等操作符認為這兩個值是全等的。第二個情況是,浮點數包含了 NaN 值,用來表示某些定義不明确的數學問題的解,例如:正無窮加負無窮。全等操作符認為 NaN 與其他任何值都不全等,包括它自己。(等式
(x !== x
) 成立的唯一情況是 x 的值為 NaN)
非嚴格相等 ==
EDIT
==
相等操作符比較兩個值是否相等,在比較前将兩個被比較的值轉換為相同類型。在轉換後(等式的一邊或兩邊都可能被轉換),最終的比較方式等同于全等操作符 === 的比較方式。 相等操作符滿足交換律。
相等操作符對于不同類型的值,進行的比較如下圖所示:
被比較值 B | |||||||
---|---|---|---|---|---|---|---|
Undefined | Null | Number | String | Boolean | Object | ||
被比較值 A | Undefined | | | | | | |
Null | | | | | | | |
Number | | | | | | | |
String | | | | | | | |
Boolean | | | | | | | |
Object | false | false | | | ToPrimitive(A) == ToNumber(B) | |
在上面的表格中,
ToNumber(A)
嘗試在比較前将參數 A 轉換為數字,這與 +A(單目運算符+)的效果相同。通過嘗試依次調用 A 的A.toString 和 A.valueOf 方法,将參數 A 轉換為原始值(Primitive)。
一般而言,根據 ECMAScript 規範,所有的對象都與
undefined
和
null
不相等。但是大部分浏覽器允許非常窄的一類對象(即,所有頁面中的
document.all
對象),在某些情況下,充當效仿
undefined
的角色。相等操作符就是在這樣的一個背景下。是以,
IsFalsy(A)
方法的值為
true
,當且僅當
A
效仿
undefined
。在其他所有情況下,一個對象都不會等于
undefined
或
null
。
var num = 0;
var obj = new String("0");
var str = "0";
var b = false;
console.log(num == num); // true
console.log(obj == obj); // true
console.log(str == str); // true
console.log(num == obj); // true
console.log(num == str); // true
console.log(obj == str); // true
console.log(null == undefined); // true
// both false, except in rare cases
console.log(obj == null);
console.log(obj == undefined);
有些開發者認為,最好永遠都不要使用相等操作符。全等操作符的結果更容易預測,并且因為沒有隐式轉換,全等比較的操作會更快。
同值相等EDIT
同值相等解決了最後一個用例:确定兩個值是否在任何情況下功能上是相同的。(這個用例示範了裡氏替換原則的執行個體。)當試圖對不可變(immutable)屬性修改時發生出現的情況:
// 向 Nmuber 構造函數添加一個不可變的屬性 NEGATIVE_ZERO
Object.defineProperty(Number, "NEGATIVE_ZERO",
{ value: -0, writable: false, configurable: false, enumerable: false });
function attemptMutation(v)
{
Object.defineProperty(Number, "NEGATIVE_ZERO", { value: v });
}
Object.defineProperty
在試圖修改不可變屬性時,如果這個屬性确實被修改了則會抛出異常,反之什麼都不會發生。例如如果 v 是 -0 ,那麼沒有發生任何變化,是以也不會抛出任何異常。但如果 v 是 +0 ,則會抛出異常。不可變屬性和新設定的值使用 same-value 相等比較。
同值相等由
Object.is
方法提供。
零值相等EDIT
與同值相等類似,不過會認為 +0 與 -0 相等。
規範中的相等、嚴格相等以及同值相等EDIT
在 ES5 中,
==
相等在 Section 11.9.3, The Abstract Equality Algorithm;
===
相等在 11.9.6, The Strict Equality Algorithm。(請參考這兩個連結,他們很簡潔易懂。提示:請先閱讀嚴格相等的算法)ES5 也提供了 same-value 相等, Section 9.12, The SameValue Algorithm ,用在 JS 引擎内部。除了 11.9.6.4 和 9.12.4 在處理數字上的不同外,它基本和嚴格相等算法相同。ES6 簡單地通過
Object.is
暴露了這個算法。
我們可以看到,使用雙等或三等時,除了 11.9.6.1 類型檢查,嚴格相等算法是相等算法的子集因為 11.9.6.2–7 對應 11.9.3.1.a–f。
了解相等比較的模型EDIT
在 ES2015 以前,你可能會說雙等和三等是“擴充”的關系。比如有人會說雙等是三等的擴充版,因為他處理三等所做的,還做了類型轉換。例如 6 == "6" 。反之另一些人可能會說三等是雙等的擴充,因為他還要求兩個參數的類型相同,是以增加了更多的限制。怎樣了解取決于你怎樣看待這個問題。
但是這種比較的方式沒辦法把 ES2015 的
Object.is
排列到其中。因為
Object.is
并不比雙等更寬松,也并不比三等更嚴格,當然也不是在他們中間。從下表中可以看出,這是由于
Object.is
處理
NaN
的不同。注意假如
Object.is(NaN, NaN)
被計算成
false
,我們就可以說他比三等更為嚴格,因為他可以區分
-0
和
+0
。但是對
NaN
的處理表明,這是不對的。
Object.is
應該被認為是有其特殊的用途,而不應說他和其他的相等更寬松或嚴格。
x | y | | | |
---|---|---|---|---|
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | ||
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
什麼時候使用 Object.is
或是三等EDIT
Object.is
總的來說,除了對待
NaN
的方式,
Object.is
唯一讓人感興趣的,是當你需要一些元程式設計方案時,它對待0的特殊方式,特别是關于屬性描述器,即你的工作需要去鏡像
Object.defineProperty
的一些特性時。如果你的工作不需要這些,那你應該避免使用
Object.is
,使用
===
來代替。即使你需要比較兩個
NaN
使其結果為
true
,總的來說編寫使用
NaN
檢查的特例函數(用舊版本ECMAScript的
isNaN方法
)也會比想出一些計算方法讓
Object.is
不影響不同符号的0的比較更容易些。
這裡是一個會差別對待-0和+0的内置方法和操作符不完全清單:
- (一進制負)
- 顯而易見,對
0一進制負操作得到
。但表達式的抽象化可能在你沒有意識到得情況下導緻-0延續傳播。例如當考慮下例時:-0
如果let stoppingForce = obj.mass * -obj.velocity
是 (或計算結果為 ),obj.velocity
就在上處産生并被指派為一個-0
.stoppingForce的值
Math.atan2
Math.ceil
Math.pow
Math.round
- 即使傳入的參數中沒有-0,這些方法的傳回值都有可能是-0。例如當用
計算Math.pow
的任何負奇指數的幂都會得到-Infinity
。詳情請參見這些方法各自的文檔。-0
Math.floor
Math.max
Math.min
Math.sin
Math.sqrt
Math.tan
- 當傳入參數中有-0時,這些方法也可能傳回-0。例如,
得出Math.min(-0, +0)
。詳情請參見這些方法各自的文檔。-0
- 這些操作符内部都使用了ToInt32算法。因為内部32位整數類型隻有一個0(沒有符号差別),-0的符号在反操作後并不會保留下來。例如
和Object.is(~~(-0), -0)
Object.is(-0 << 2 >> 2, -0)
.都會得到false
~
<<
>>
在未考慮0的符号的情況下依賴于
Object.is
是危險的。當然,如果本意就是區分-0和+0的話,
Object.is
能按照期望完成工作。