曾经对我来说,等于就是等于,所以第一次接触到 JS 中的三等号时我感到很困惑。大部分编程语言中都有双等号"==",但没有三等号"==="。
下面是一个比较官方的双等号与三等号的差别:
"如果需要判断运算数是否为同一类型并且值相等,则使用严格相等运算符[===]。
否则,使用标准相等运算符[==],它允许你比较两个运算数的值,即使它们类型不同。"
--MDN web docs
在认为理解它的关键就在于了解“类型强制转换”,它是“动态类型语言” Vanilla JAVAScript 中的一个奇怪的概念。
之所以会有类型强制转换是因为 JS 想要防御编程错误,比如拿一个数字和一个字符串比较:
“当在 JavaScript 中使用双等号时我们测试的是不严格的相等。双等号还会带来类型强制转换。
强制转换的意思是只有当运算数被转换为相同类型时它们才能被比较。”
换句话说,由于有类型转换,不同类型的值可以通过 == 被比较,但一个字符串和一个数字则永远不会用 === 严格相等。
举个例子:
console.log(Number("7") - 7 == 0); // true console.log(Number("7") - 7 === 0); // true console.log(Number("7") - 7 === Number("0")); // true console.log("7" - 7 == "0"); // true console.log("7" - 7 === "0"); // false
JavaScript 中的真与假
在 JavaScript 中我们有六个代表假的值:false, 0, ""(空字符串),null, undefined 和 NaN(Not A Number),这六个假值互相是什么关系呢?
“将 false,0和""用(==)相比较,结果为真,它们都不严格相等;null 和 undefined 只和自己相等;NaN 不与任何假值相等,包括它自己。”
// All permutations of falsy comparisons in JavaScript by Dr. Derek Austin console.log(false == false); // true console.log(false === false); // true console.log(false == 0); // true -- false, 0, and "" are loosely equal console.log(false === 0); // false console.log(false == ""); // true -- false, 0, and "" are loosely equal console.log(false === ""); // false console.log(false == null); // false console.log(false === null); // false console.log(false == undefined); // false console.log(false === undefined); // false console.log(false == NaN); // false console.log(false === NaN); // false console.log(0 == 0); // true console.log(0 === 0); // true console.log(0 == ""); // true -- false, 0, and "" are loosely equal console.log(0 === ""); // false console.log(0 == null); // false console.log(0 === null); // false console.log(0 == undefined); // false console.log(0 === undefined); // false console.log(0 == NaN); // false console.log(0 === NaN); // false console.log("" == ""); // true console.log("" === ""); // true console.log("" == null); // false console.log("" === null); // false console.log("" == undefined); // false console.log("" === undefined); // false console.log("" == NaN); // false console.log("" === NaN); // false console.log(null == null); // true console.log(null === null); // true console.log(null == undefined); // true -- null loosely equals undefined console.log(null === undefined); // false console.log(null == NaN); // false console.log(null === NaN); // false console.log(undefined == undefined); // true console.log(undefined === undefined); // true console.log(undefined == NaN); // false console.log(undefined === NaN); // false console.log(NaN == NaN); // false -- NaN nothing equals NaN, and console.log(NaN === NaN); // false -- NaN doesn't equal itself
虽然 JS 中的假值这么奇葩,但幸好除了这些假值以外所有值都为真:
“在 JavaScript 中,真值就是在布尔类型上下文中所有被认为是"true"的值。所有的值皆为真,除非它被定义为假,比如 false,0,"",null,undefined 和 NaN。”
ECMAScript 中的情况呢?
如果你感兴趣的话,下面是 JavaScript 所使用的具体算法。
严格相等运算符
严格相等运算符(=== 与 !==)使用严格相等比较算法来比较两个运算数:
11.9.6 严格相等比较算法 当比较 x===y 时,x 与 y 为值,表达式返回 true 或 false,表达式执行方式如下: 1. 如果 Type(x) 和 Type(y) 不同, 返回 false. 2. 如果 Type(x) 为 Undefined, 返回 true. 3. 如果 Type(x) 为 Null, 返回 true. 4. 如果 Type(x) 为数字,那么 → a. 如果 x 为 NaN, 返回 false. → b. 如果 y 为 NaN, 返回 false. → c. 如果 x 与 y 的数字值相等, 返回 true. → d. 如果 x 为 +0 而 y 为 −0, 返回 true. → e. 如果 x 为 −0 而 y 为 +0, 返回 true. → f. 返回 false. 5. 如果 Type(x) 为字符串,如果 x 与 y 中的字符顺序完全相同(长度相同,字符位置相同),则返回 true;否则返回 false. 6. 如果 Type(x) 为布尔类型, 如果 x 与 y 皆为真或假,则返回 true;否则,返回 false. 7. 如果 x 和 y 引用了同一个对象,返回 true;否则返回 false. NOTE — This algorithm d如果fers from the SameValue Algorithm (9.12) in its treatment of signed zeroes and NaNs.
标准相等运算符
标准相等运算符(== 和 !=)使用抽象相等比较算法来比较两个运算数:
11.9.3 抽象相等比较算法(Abstract Equality Comparison Algorithm) 比较 x == y, x 和 y 为值, 表达式返回 true 或 false. 表达式执行如下: 1. 如果 Type(x) 与 Type(y) 相同, 那么 → a. 如果 Type(x) 为 Undefined, 返回 true. → b. 如果 Type(x) 为 Null, 返回 true. → c. 如果 Type(x) 为数字, 则 → → i. 如果 x 为 NaN, 返回 false. → → ii. 如果 y 为 NaN, 返回 false. → → iii. 如果 x 与 y 数字值相等, 返回 true. → → iv. 如果 x 为 +0 并且 y 为 −0, 返回 true. → → v. 如果 x 为 −0 并且 y 为 +0, 返回 true. → → vi. 返回 false. → d. 如果 Type(x) 是字符串, 如果 x 与 y 中的字符顺序完全相同(长度相同,字符位置相同),则返回 true;否则返回 false. → e. 如果 Type(x) 为布尔类型, 如果 x 与 y 皆为真或假,则返回true;否则返回 false. → f. 如果 x 与 y 引用了同一个对象,返回 true;否则返回 false. 2. 如果 x 为 null 并且 y 为 undefined, 返回 true. 3. 如果 x 为 undefined 并且 y 为 null, 返回 true. 4. 如果 Type(x) 为数字并且 Type(y) 为字符串, 返回 x == ToNumber(y) 的结果. 5. 如果 Type(x) 为字符串并且 Type(y) 为数字, 返回 ToNumber(x) == y 的结果. 6. 如果 Type(x) 为布尔类型, 返回 ToNumber(x) == y 的结果. 7. 如果 Type(y) 为布尔类型, 返回 x == ToNumber(y) 的结果. 8. 如果 Type(x) 为数字或字符串,并且 Type(y) 为 Object, 返回 x == ToPrimitive(y) 的结果. 9. 如果 Type(x) 为 Object 并且 Type(y) 为数字或字符串, 返回 ToPrimitive(x) == y 的结果. 10. 返回 false. NOTE 1 — Given the above definition of equality: • String comparison can be forced by: "" + a == "" + b. • Numeric comparison can be forced by: +a == +b. • Boolean comparison can be forced by: !a == !b. NOTE 2 — The equality operators maintain the following invariants: • A != B is equivalent to !(A == B). • A == B is equivalent to B == A, except in the order of evaluation of A and B. NOTE 3 The equality operator is not always transitive. For example, there might be two distinct String objects, each representing the same String value; each String object would be considered equal to the String value by the == operator, but the two String objects would not be equal to each other. For example: • new String("a") == "a" and "a" == new String("a")are both true. • new String("a") == new String("a") is false. NOTE 4 Comparison of Strings uses a simple equality test on sequences of code unit values. There is no attempt to use the more complex, semantically oriented definitions of character or string equality and collating order defined in the Unicode spec如果ication. Therefore Strings values that are canonically equal according to the Unicode standard could test as unequal. In effect this algorithm assumes that both Strings are already in normalized form.
结论
通常来说,我比较喜欢用 === 和 !==,除非碰到了必须使用 == 和 != 的情况,比如检查空值。
另外,在检查空值时,牢记 null 和 undefined 使用双等号比较时是相等的这点很有用。