在 JavaScript 环境下,可让表达式 a == true && a == false 为 true 吗?html
就像下面这样,能够在控制台打印出 ’yeah':this
// code here if (a == true && a == false) { console.log('yeah'); }
JavaScript 是一门类型松散的语言,在使用 ==
进行比较时,假若左右类型不一致,是会进行类型装换的。首先来了解一下宽松相等的概念,lua
先看看 ECMA 5.1 的规范,包含 toPrimitive
:code
If Type(x) is the same as Type(y), thenhtm
If Type(x) is Number, then对象
Table 10 — ToPrimitive Conversionsip
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. |
对于下述表达式:ci
x == y
类型不一样开发
至于 ToPrimitive
,即求原始值,能够简单理解为进行 valueOf()
和 toString()
操做。字符串
稍后咱们再详细剖析,接下来先看一个问题。
就像这样:
// 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
贴个规范:8.12.8 [[DefaultValue]] (hint)
若是是 Date
求原始值,则 hint 是 String
,其余均为 Number
,即先调用 valueOf()
再调用 toString()
。
若是 hint 为 Number
,具体过程以下:
valueOf()
方法,若是值是原值则返回toString()
方法,若是值是原值则返回// 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 中,有什么变化呢
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'); }