Equality(==)
相等操作符比较两个值是否相等,在比较前将两个被比较的值转换为相同类型。在转换后(等式的一边或两边都可能被转换),最终的比较方式等同于全等操作符 === 的比较方式。 相等操作符满足交换律。
等于运算符(==)检查其两个操作数是否相等,并返回Boolean结果。与严格相等运算符(===)不同,它会尝试强制类型转换并且比较不同类型的操作数。
console.log(1 == 1); // true
console.log('hello' == 'hello'); // true
console.log('1' == 1); // true
console.log(0 == false); // true
语法
x == y
描述
相等运算符(== 和 !=)使用抽象相等比较算法比较两个操作数。
如果两个操作数都是对象,则仅当两个操作数都引用同一个对象时才返回true。
如果一个操作数是null,另一个操作数是undefined,则返回true。
-
如果两个操作数是不同类型的,就会尝试在比较之前将它们转换为相同类型:
当数字与字符串进行比较时,会尝试将字符串转换为数字值。
如果操作数之一是Boolean,则将布尔操作数转换为1或0。
1.如果是 true,则转换为1。
2.如果是 false,则转换为0。如果操作数之一是对象,另一个是数字或字符串,会尝试使用对象的valueOf()和toString()方法将对象转换为原始值。
如果操作数具有相同的类型,则将它们进行如下比较:
String:true仅当两个操作数具有相同顺序的相同字符时才返回。
Number:true仅当两个操作数具有相同的值时才返回。+0并被-0视为相同的值。如果任一操作数为NaN,则返回false。
Boolean:true仅当操作数为两个true或两个false时才返回true。
此运算符与严格等于(===)运算符之间最显着的区别在于,严格等于运算符不尝试类型转换。相反,严格相等运算符始终将不同类型的操作数视为不同。
示例
没有类型转换的比较
console.log("hello" == "hello"); // true
console.log(0 == 0); // true
console.log(1 == 1); // true
console.log(+0 == -0); // true
console.log(false == false); // true
console.log(Number.MAX_SAFE_INTEGER == Number.MAX_SAFE_INTEGER); // true
console.log(null == null); // true
console.log(undefined == undefined); // true
console.log(NaN == NaN); // false
console.log({} == {}); // false
console.log([] == []); // false
console.log(Symbol() == Symbol()); // false
console.log(Symbol(1) == Symbol(1)); // false
console.log(newDate() == newDate()); // false
与类型转换比较
"1" == 1; // true
1 == "1"; // true
0 == false; // true
0 == null; // false
0 == undefined; // false
null == undefined; // true
const number1 = new Number(3);
const number2 = new Number(3);
number1 == 3; // true
number1 == number2; // false
var num = 0;
var obj = new String("0");
var str = "0";
var b = false;
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); // true
console.log(obj == undefined); // true
对象比较
const object1 = {"key": "value"}
const object2 = {"key": "value"};
console.log(object1 == object2) // false
console.log(object2 == object2) // true
比较字符串和String对象
请注意,使用构造的字符串new String()是对象。如果将其中之一与字符串文字进行比较,则该String对象将被转换为字符串文字并对其内容进行比较。但是,如果两个操作数都是String对象,则将它们作为对象进行比较,并且必须引用相同的对象才能进行比较:
const string1 = "hello";
const string2 = String("hello");
const string3 = new String("hello");
const string4 = new String("hello");
console.log(string1 == string2); // true
console.log(string1 == string3); // true
console.log(string1 == string4); // true
console.log(string2 == string3); // true
console.log(string2 == string4); // true
console.log(string3 == string4); // false
console.log(string4 == string4); // true
比较日期和字符串
const d = new Date('December 17, 1995 03:24:00');
const s = d.toString(); // for example: "Sun Dec 17 1995 03:24:00 GMT-0800 (Pacific Standard Time)"
console.log(d == s); // true
相等操作符对于不同类型的值,进行的比较如下图所示:
在上面的表格中,ToNumber(A) 尝试在比较前将参数 A 转换为数字,这与 +A(单目运算符+)的效果相同。ToPrimitive(A)通过尝试调用 A 的 A.toString() 和 A.valueOf() 方法,将参数 A 转换为原始值(Primitive)。
一般而言,根据 ECMAScript 规范,所有的对象都与 undefined 和 null 不相等。但是大部分浏览器允许非常窄的一类对象(即,所有页面中的 document.all 对象),在某些情况下,充当效仿 undefined 的角色。相等操作符就是在这样的一个背景下。因此,IsFalsy(A) 方法的值为 true ,当且仅当 A 效仿 undefined。在其他所有情况下,一个对象都不会等于 undefined 或 null。
总结:Equality(==) 相等符号的做法是:如果两个操作数是不同类型的,就会尝试在比较之前将它们转换为相同类型;如果操作数具有相同的类型,则将它们进行如下比较。可以看出对 NaN==NaN 的输出是false、0==false 的输出是true、null==undefined 的输出是true、+0===-0 的输出是true,并不是很严谨。
全等 Strict Equality(===)
全等操作符比较两个值是否相等,两个被比较的值在比较前都不进行隐式转换。如果两个被比较的值具有不同的类型,这两个值是不全等的。否则,如果两个被比较的值类型相同,值也相同,并且都不是 number 类型时,两个值全等。最后,如果两个值都是 number 类型,当两个都不是 NaN,并且数值相同,或是两个值分别为 +0 和 -0 时,两个值被认为是全等的。
全等运算符 (===) 会检查它的两个操作数是否相等,并且返回一个布尔值结果。与相等运算符不同,全等运算符总是认为不同类型的操作数是不同的。
console.log(1 === 1); // true
console.log('hello' === 'hello'); // true
console.log('1' === 1); // alse
console.log(0 === false); // false
语法
x === y
描述
全等运算符(===和 !==)使用全等比较算法来比较两个操作数。
- 如果操作数的类型不同,则返回 false。
- 如果两个操作数都是对象,只有当它们指向同一个对象时才返回 true。
- 如果两个操作数都为 null,或者两个操作数都为 undefined,返回 true。
- 如果两个操作数有任意一个为 NaN,返回 false。
- 否则,比较两个操作数的值:
数字类型必须拥有相同的数值。+0 和 -0 会被认为是相同的值。
字符串类型必须拥有相同顺序的相同字符。
布尔运算符必须同时为 true 或同时为 false。
全等运算符与相等运算符(==)最显著的区别是,如果操作数的类型不同,== 运算符会在比较之前尝试将它们转换为相同的类型。
示例
比较相同类型的操作数
console.log("hello" === "hello"); // true
console.log("hello" === "hello"); // false
console.log(3 === 3); // true
console.log(3 === 4); // false
console.log(true === true); // true
console.log(true === false); // false
console.log(null === null); // true
console.log(undefined === undefined); // true
console.log(NaN === NaN); // false
console.log(+0 === -0); // true
比较不同类型的操作数
console.log("3" === 3); // false
console.log(true === 1); // false
console.log(null === undefined); // false
var num = 0;
var obj = new String("0");
var str = "0";
var b = false;
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
比较对象
const object1 = {
name: "hello"
}
const object2 = {
name: "hello"
}
console.log(object1 === object2); // false
console.log(object1 === object1); // true
在日常中使用全等操作符几乎总是正确的选择。对于除了数值之外的值,全等操作符使用明确的语义进行比较:一个值只与自身全等。对于数值,全等操作符使用略加修改的语义来处理两个特殊情况:第一个情况是,浮点数 0 是不分正负的。区分 +0 和 -0 在解决一些特定的数学问题时是必要的,但是大部分情况下我们并不用关心。全等操作符认为这两个值是全等的。第二个情况是,浮点数包含了 NaN 值,用来表示某些定义不明确的数学问题的解,例如:正无穷加负无穷。全等操作符认为 NaN 与其他任何值都不全等,包括它自己。(等式 (x !== x) 成立的唯一情况是 x 的值为 NaN)
总结:Strict equality(===) 严格相等符号是:先比较两个操作数的类型,再去比较两个操作数的值。可以看出对 NaN===NaN 的输出是false、+0===-0 的输出是true,虽然相对于(==)相等符号来说比较严格,但还是不能判断所有的情况。
Objec.is()
Object.is() 方法判断两个值是否为同一个值。
语法
Object.is(value1, value2);
参数
value1
被比较的第一个值。
value2
被比较的第二个值。
返回值
一个布尔值,表示两个参数是否是同一个值。
描述
Object.is() 方法判断两个值是否为同一个值,如果满足以下任意条件则两个值相等:
- 都是 undefined
- 都是 null
- 都是 true 或都是 false
- 都是相同长度、相同字符、按相同顺序排列的字符串
- 都是相同对象(意味着都是同一个对象的值引用)
- 都是数字且
都是 +0
都是 -0
都是 NaN
都是同一个值,非零且都不是 NaN
Object.is() 与 == 不同。== 运算符在判断相等前对两边的变量(如果它们不是同一类型)进行强制转换(这种行为将 "" == false 判断为 true),而 Object.is 不会强制转换两边的值。
Object.is() 与 === 也不相同。差别是它们对待有符号的零和 NaN 不同,例如,=== 运算符(也包括 == 运算符)将数字 -0 和 +0 视为相等,而将 Number.NaN 与 NaN 视为不相等。
示例
// Case 1: Evaluation result is the same as using ===
Object.is(25, 25); // true
Object.is('foo', 'foo'); // true
Object.is('foo', 'bar'); // false
Object.is(null, null); // true
Object.is(undefined, undefined); // true
Object.is(window, window); // true
Object.is([], []); // false
var foo = { a: 1 };
var bar = { a: 1 };
Object.is(foo, foo); // true
Object.is(foo, bar); // false
// Case 2: Signed zero
Object.is(0, -0); // false
Object.is(+0, -0); // false
Object.is(-0, -0); // true
Object.is(0n, -0n); // true
// Case 3: NaN
Object.is(NaN, 0/0); // true
Object.is(NaN, Number.NaN) // true
Polyfill
ES5 可以通过下面的代码,实现和 Object.is() 方法一样的效果。
if (!Object.is) {
Object.defineProperty(Object, "is", {
value: function (x, y) {
// SameValue algorithm
if (x === y) {
// return true if x and y are not 0, OR
// if x and y are both 0 of the same sign.
// This checks for cases 1 and 2 above.
return x !== 0 || 1 / x === 1 / y;
} else {
// return true if both x AND y evaluate to NaN.
// The only possibility for a variable to not be strictly equal to itself
// is when that variable evaluates to NaN (example: Number.NaN, 0/0, NaN).
// This checks for case 3.
return x !== x && y !== y;
}
}
});
}
总结:Object.is() 与 === 的判断结果基本相同,差别是它们对待有符号的 0 和 NaN 不同;并且和 == 运算符相比,Object.is 不会强制转换两边的值。
如何选择相等性判断
ES2015中有四种相等算法:
- 抽象(非严格)相等比较 (==)
- 严格相等比较 (===): 用于 Array.prototype.indexOf, Array.prototype.lastIndexOf, 和 case-matching
- 同值零: 用于 %TypedArray% 和 ArrayBuffer 构造函数、以及Map和Set操作,并将用于 ES2016/ES7 中的String.prototype.includes
- 同值: 用于所有其他地方
JavaScript提供三种不同的值比较操作:
- 严格相等比较 (也被称作"strict equality", "identity", "triple equals"),使用 ===
- 抽象相等比较 ("loose equality","double equals") ,使用 ==
- 以及 Object.is (ECMAScript 2015 / ES6 新特性)
选择使用哪个操作取决于你需要什么样的比较。
简而言之,在比较两件事情时,双等号将执行类型转换; 三等号将进行相同的比较,而不进行类型转换 (如果类型不同, 只是总会返回 false ); 而Object.is的行为方式与三等号相同,但是对于NaN和-0和+0进行特殊处理,所以最后两个不相同,而Object.is(NaN,NaN)将为 true。(通常使用双等号或三等号将NaN与NaN进行比较,结果为false) 请注意,所有这些之间的区别都与其处理原语有关; 这三个运算符的原语中,没有一个会比较两个变量是否结构上概念类似。对于任意两个不同的非原始对象,即便他们有相同的结构, 以上三个运算符都会计算得到 false 。