比较少见的情况
- • 如何让同时 a == 1 && a == 2 && a == 3?
- • 其中不能用同时,因为 a = 1 在 a = 2 之前执行,a = 2 在 a = 3 之前执行。
- • 如下代码:
// 方法一: var a = { i: 1, [Symbol.toPrimitive]() { return this.i++; } }; if (a == 1 && a == 2 && a == 3) { console.log(a); // { i: 4, valueOf: [Function: valueOf] } 输出 a 对象,注意 i 的值 } // 方法二: var a = { i: 1, valueOf() { return this.i++; }, }; if (a == 1 && a == 2 && a == 3) { console.log(a); // { i: 4, valueOf: [Function: valueOf] } 输出 a 对象,注意 i 的值 } // 如果让 a.valueOf() 每次调用都产生副作用,比如第一次返回 1, 第二次返回 2,以此类推,就会产生这种情况。 // 方法三: var a = { i: 1, toString() { return this.i++; }, }; if (a == 1 && a == 2 && a == 3) { console.log(a); // { i: 4, valueOf: [Function: valueOf] } 输出 a 对象,注意 i 的值 }
- • 在 == 隐式强制类型转换中最令人头疼的就是假值得相等比较。
"0" == null; // false "0" == undefined; // false "0" == false; // true -- 晕! "0" == NaN; // false "0" == 0; // true "0" == ""; // false false == null; // false false == undefined; // false false == NaN; // false false == 0; // true -- 晕! false == ""; // true -- 晕! false == []; // true -- 晕! false == {}; // false "" == null; // false "" == undefined; // false "" == NaN; // false "" == 0; // true -- 晕! "" == []; // true -- 晕! "" == {}; // false 0 == null; // false 0 == undefined; // false 0 == NaN; // false 0 == []; // true -- 晕! 0 == {}; // false
- • 以上的这 24种情况 中有 17 中我们比较好理解,但有 7 中不好理解。
- • 那
如何安全使用 == 操作符呢?
- 1.
如果两边的值有 true 或 false, 千万不要使用 ==
- 2.
如果两边的值有 []、""、0, 千万不要使用 ==
抽象关系比较
- • 在我们日常的代码中,可能会存在 a < b 这种情况的判断,但这里面也涉及了隐式强制类型转换,有必要要了解一下。
- • 会发生隐式强制类型转换的算法只会针对于
a < b, a = "" > b 会被处理为 b <>
- • ES5 规则:
- •
比较双方会首先调用 toPromitive,如果结果中出现非字符串,就根据 toNumber 的规则将双方强制类型转换为数字进行比较
// 如下: var a = 42; var b = "43"; a < b; // true 这里为什么会返回 true, 先保留疑惑,后面会解答 b < a; // false // 再比如:如果比较双方都是字符串,则按照字母顺序进行比较: var a = ["42"]; var b = ["043"]; a < b; // false b < a; // true // 再比如:如果比较双方都是字符串, 则会进行 toPromitive 操作 var a = {b: 42}; var b = {b: 43}; a < b; // false b < a; // false // 因为 a = [object Object], b 也是 [object, Object],所以按照字母顺序排序 a < b, b < a 不成立。 // 再比如: var a = {b: 42}; var b = {b: 43}; a < b; // false a == b; // false a > b; // false a <= b; // true a >= b; // true // 此时你可能会好奇 a < b 和 a == b 都是 false,为什么 a <= b 和 a > b 为 true? // 因为根据规则 a <= b 会被处理为 b < a, 然后将结果反转。(如果没懂,回头看这段实例代码)
- • 上面的结果可能与我们设想的大相径庭,相等比较有严格相等,关系比较却没有严格相等,也就是说
如果要避免 a < b 之间的隐式强制类型转转,就只能确保 a 和 b 为相同的类型, 或进行显示的强制类型转换。
小结
- 1. 值类型转换规则:
- •
toString: 对于普通对象来说,除非自定义,否则都会调用其内部的 toString() 方法。
- •
toNumber: 在使用 Number() 或 toNumber() 方法将一个字符串转换为数字时,如果字符串中出现非数字字符,则会返回 NaN。
- •
toBoolean: 除 undefined、null、false、+0、-0 和 NaN、"" 都为真值
- •
toPromitive: 如果检查该值是否有 valueOf 方法,看是否会返回原始值,如果返回值是原始值,则直接使用。否则,就使用 toString 方法,如果 toString 方法返回的是原始值,则直接使用,否则抛出 TypeError 错误。
- 2. 显/隐式强制类型转换:
- •
如果 + 运算符中其中一个操作数是字符串,则执行字符串拼接,否则执行加法运算
。 - •
~(非) 运算符: ~ 会返回 2 的补码, 而 ~x 大致等于 -(x + 1)
// ~x 大致等于 -(x + 1)。 ~42; // -(42 + 1) ==> -43
- 1. || 与 &&:
- •
|| 和 && 操作符会对第一个操作数进行条件判断,且会对第一个操作数进行隐式类型转换(会通过 toBoolean 操作),然后再进行条件判断。
- •
|| 运算符,如果条件判断结果为true, 就返回第一个操作数的结果。如果为 false, 就返回第二个操作数的结果
。 - •
&& 运算符则相反,如果条件判断结果为 true 就返回第二个操作数结果,如果为 false, 就返回第一个操作数的结果
。
a || b; // 大致相当于 a ? a : b; a && b; // 大致相当于 a ? b : a;
- 1. 严格相等(===) 与宽松相等(==) 有一个重要的区别,特别是在判断条件上(
在于对操作数类型不同时他们的处理方式不同
):== 允许在相等比较中进行强制类型转换,而 === 不允许
。
- •
在两个值类型相同情况下,使用 == 与 === 没有区别
- •
在两个值类型不同情况下,就要考虑是否有没有强制类型转换的必要,有就用 ==, 没有就用 ===
- 2. 字符串与数字之间的比较规则:
- •
如果 Type(x) 为数字,Type(y) 为字符串,则返回 x == toNumber(y) 的结果
- •
如果 Type(x) 为字符串,Type(y) 是数字,则返回 toNumber(x) == y 的结果
var a = 42; var a = "42"; a === b; // false a == b; // true
- 1. 其他类型与布尔值的比较规则:(宽松相等(==) 判断时两边的布尔值会进行 toNumber 操作)
- •
如果 Type(x) 是布尔类型,则返回 toNumber(x) == y 的结果
- •
如果 Type(y) 是布尔类型,则返回 x == toNumber(y) 的结果
var a = "42"; var b = true; a == b; // false var a = "42"; // 不要这样用,条件判断不成立: if (a == true) { // .. } // 也不要这样用,条件判断不成立: if (a === true) { // .. } // 这样的显式用法没问题: if (a) { // .. } // 这样的显式用法更好: if (!!a) { // .. } // 这样的显式用法也很好: if (Boolean( a )) { // .. }
- 1. null 与 undefined 的比较规则:
- •
如果 x 为 null, y 为 undefined, 则结果为 true
- •
如果 x 为 undefined, y 为 null, 则结果为 true
var a = null; var b; a == b; // true a == null; // true b == null; // true null == undefined; // true null == null; // true undefined == undefined; // true a == false; // false b == false; // false a == ""; // false b == ""; // false a == 0; // false b == 0; // false
- • 所以我们
可将 null 和 undefined 作为等价来处理
。
var a = doSomething(); if (a == null) { // .. }
- 1. 对象与非对象之间的相等比较规则:
- •
如果 Type(x) 是字符串或数字,Type(y) 是对象,则返回 x == toPromitive(y) 的结果
- •
如果 Type(x) 是对象,Type(y) 是字符串或数字,则返回 toPromitive(x) == y 的结果
- 2. 宽松相等(==) 的假真值比较:
"0" == null; // false "0" == undefined; // false "0" == false; // true -- 晕! "0" == NaN; // false "0" == 0; // true "0" == ""; // false false == null; // false false == undefined; // false false == NaN; // false false == 0; // true -- 晕! false == ""; // true -- 晕! false == []; // true -- 晕! false == {}; // false "" == null; // false "" == undefined; // false "" == NaN; // false "" == 0; // true -- 晕! "" == []; // true -- 晕! "" == {}; // false 0 == null; // false 0 == undefined; // false 0 == NaN; // false 0 == []; // true -- 晕! 0 == {}; // false
- 1. 如何安全使用 宽松相等(==) 操作符呢?
- 1.
如果两边的值有 true 或 false, 千万不要使用 ==;
- 2.
如果两边的值有 []、""、0, 千万不要使用 ==;
- 2. 抽象关系比较存在隐式的强制类型转换,通常存在于
a < b, a = "" > b 会被处理为 b <>
判断中,其中一个很重要的点是,会将结果反转
。
- • 那
如何规避掉上述隐式的强制类型转换
?
- •
确保 a 和 b 为相同的类型, 或进行显示的强制类型转换。
- 1. 如何让同时 a == 1 && a == 2 && a == 3?
- • 其中不能用同时,因为 a = 1 在 a = 2 之前执行,a = 2 在 a = 3 之前执行。
// 方法一: var a = { i: 1, [Symbol.toPrimitive]() { return this.i++; } }; if (a == 1 && a == 2 && a == 3) { console.log(a); // { i: 4, valueOf: [Function: valueOf] } 输出 a 对象,注意 i 的值 } // 方法二: var a = { i: 1, valueOf() { return this.i++; }, }; if (a == 1 && a == 2 && a == 3) { console.log(a); // { i: 4, valueOf: [Function: valueOf] } 输出 a 对象,注意 i 的值 } // 如果让 a.valueOf() 每次调用都产生副作用,比如第一次返回 1, 第二次返回 2,以此类推,就会产生这种情况。 // 方法三: var a = { i: 1, toString() { return this.i++; }, }; if (a == 1 && a == 2 && a == 3) { console.log(a); // { i: 4, valueOf: [Function: valueOf] } 输出 a 对象,注意 i 的值 }
特殊字符描述
•问题标注 Q:(question)
•答案标注 R:(result)
•注意事项标准:A:(attention matters)
•详情描述标注:D:(detail info)
•总结标注:S:(summary)
•分析标注:Ana:(analysis)
•提示标注:T:(tips)