在 JavaScript 环境下,能够让表达式 a == true && a == false 为 true 吗?
就像下面如许,能够在控制台打印出 ’yeah’:
// code here
if (a == true && a == false) {
console.log('yeah');
}
JavaScript 是一门范例松懈的言语,在运用 ==
举行比较时,倘使摆布范例不一致,是会举行范例装换的。起首来相识一下宽松相称的观点,
宽松相称 ==
先看看 ECMA 5.1 的范例,包括 toPrimitive
:
范例
11.9.3 The Abstract Equality Comparison Algorithm
If Type(x) is the same as Type(y), then
- If Type(x) is Undefined, return true.
- If Type(x) is Null, return true.
If Type(x) is Number, then
- If x is NaN, return false.
- If y is NaN, return false.
- If x is the same Number value as y, return true.
- If x is +0 and y is −0, return true.
- If x is −0 and y is +0, return true.
- Return false.
- If Type(x) is String, then return true if x and y are exactly the same sequence of characters (same length and same characters in corresponding positions). Otherwise, return false.
- If Type(x) is Boolean, return true if x and y are both true or both false. Otherwise, return false.
- Return true if x and y refer to the same object. Otherwise, return false.
- If x is null and y is undefined, return true.
- If x is undefined and y is null, return true.
- If Type(x) is Number and Type(y) is String, return the result of the comparison x == ToNumber(y).
- If Type(x) is String and Type(y) is Number, return the result of the comparison ToNumber(x) == y.
- If Type(x) is Boolean, return the result of the comparison ToNumber(x) == y.
- If Type(y) is Boolean, return the result of the comparison x == ToNumber(y).
- If Type(x) is either String or Number and Type(y) is Object, return the result of the comparison x == ToPrimitive(y).
- If Type(x) is Object and Type(y) is either String or Number, return the result of the comparison ToPrimitive(x) == y.
- Return false.
9.1 ToPrimitive
Table 10 — ToPrimitive Conversions
Input Type | Result |
---|---|
Undefined | The result equals the input argument (no conversion). |
Null | The result equals the input argument (no conversion). |
Boolean | The result equals the input argument (no conversion). |
Number | The result equals the input argument (no conversion). |
String | The result equals the input argument (no conversion). |
Object | Return a default value for the Object. The default value of an object is retrieved by calling the [[DefaultValue]] internal method of the object, passing the optional hint PreferredType. The behaviour of the [[DefaultValue]] internal method is defined by this specification for all native ECMAScript objects in 8.12.8. |
稍作总结
关于下述表达式:
x == y
- 范例雷同,推断的就是 x === y
范例差别
- 假如 x,y 个中一个是布尔值,将这个布尔值举行 ToNumber 操纵
- 假如 x,y 个中一个是字符串,将这个字符串举行 ToNumber 操纵
- 若果 x,y 一方为对象,将这个对象举行 ToPrimitive 操纵
至于 ToPrimitive
,即求原始值,能够简朴明白为举行 valueOf()
和 toString()
操纵。
稍后我们再细致理会,接下来先看一个问题。
Question:是不是存在如许一个变量,满足 x == !x
就像如许:
// code here
if (x == !x) {
console.log('yeah');
}
能够很多人会想到下面这个,毕竟我们也曾热衷于种种奇技淫巧:
[] == ![] // true
但答案毫不仅仅范围于此,比方:
var x = new Boolean(false);
if (x == !x) {
console.log('yeah');
}
明白这个问题,基础高低面的这些例子都不是问题了。
宽松相称的栗子
9 == '9'
9 == '9x'
9 == true
9 == undefined
9 == null
0 == undefined
0 == null
undefined == null
0 == false
'' == false
'1' == true
'9' == true
9 == [9]
'9' == [9]
'9' == [9, 4]
'9,4' == [9, 4]
[] == []
[] == ![]
![] == ![]
[] == {}
[] == !{}
{} == ![]
{} == {}
{} == !{}
9 == { toString() { return 9 }}
9 == { valueOf() { return 9 }}
9 == { a: 9 }
9 == {}
'[object Object]' == {}
在来看看什么是 ToPrimitive
ToPrimitive
贴个范例:8.12.8 [[DefaultValue]] (hint)
假如是 Date
求原始值,则 hint 是 String
,其他均为 Number
,即先挪用 valueOf()
再挪用 toString()
。
假如 hint 为 Number
,详细历程以下:
- 挪用对象的
valueOf()
要领,假如值是原值则返回 - 不然,挪用对象的
toString()
要领,假如值是原值则返回 - 不然,抛出 TypeError 毛病
// valueOf 和 toString 的挪用递次
var a = {
valueOf() {
console.log('valueof')
return []
},
toString() {
console.log('toString')
return {}
}
}
a == 0
// valueof
// toString
// Uncaught TypeError: Cannot convert object to primitive value
// Date 范例先 toString,后 valueOf
var t = new Date('2018/04/01');
t.valueOf = function() {
console.log('valueof')
return []
}
t.toString = function() {
console.log('toString')
return {}
}
t == 0
// toString
// valueof
// Uncaught TypeError: Cannot convert object to primitive value
到目前为止,上面的都是 ES5 的范例,那末在 ES6 中,有什么变化呢
ES6 中 ToPrimitive
7.1.1ToPrimitive ( input [, PreferredType] )
在 ES6 中吗,是能够自定义 @@toPrimitive 要领的,是 Well-Known Symbols(§6.1.5.1)中的一个。JavaScript 还内建了一些在 ECMAScript 5 之前没有暴露给开发者的 symbol,它们代表了内部言语行动。
// 没有 Symbol.toPrimitive 属性的对象
var obj1 = {};
console.log(+obj1); // NaN
console.log(`${obj1}`); // '[object Object]'
console.log(obj1 + ''); // '[object Object]'
// 具有 Symbol.toPrimitive 属性的对象
var obj2 = {
[Symbol.toPrimitive](hint) {
if (hint == 'number') {
return 10;
}
if (hint == 'string') {
return 'hello';
}
return true;
}
};
console.log(+obj2); // 10 -- hint is 'number'
console.log(`${obj2}`); // 'hello' -- hint is 'string'
console.log(obj2 + ''); // 'true' -- hint is 'default'
有了上述铺垫,答案就呼之欲出了
最初问题的答案
var a = {
flag: false,
toString() {
return this.flag = !this.flag;
}
}
或许运用 valueOf()
:
var a = {
flag: false,
valueOf() {
return this.flag = !this.flag;
}
}
或许是直接转变 ToPrimitive 行动:
// 实在只需设置 default 即可
var a = {
flag: false,
[Symbol.toPrimitive](hint) {
if (hint === 'number') {
return 10
}
if (hint === 'string') {
return 'hello'
}
return this.flag = !this.flag
}
}
假如是严厉相称呢
然则,有无方法是严厉相称,即:
// code here
if (a === true && a === false) {
console.log('yeah');
}
答案是:有。
运用 defineProperty
,能够有不少朋侪一开始就想到这类体式格局,简朴贴一下:
let flag = false
Object.defineProperty(window, 'a', {
get() {
return (flag = !flag)
}
})
if (a === true && a === false) {
console.log('yeah');
}