魔幻言语 JavaScript 系列之 a == true && a == false

在 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

  1. If Type(x) is the same as Type(y), then

    1. If Type(x) is Undefined, return true.
    2. If Type(x) is Null, return true.
    3. If Type(x) is Number, then

      1. If x is NaN, return false.
      2. If y is NaN, return false.
      3. If x is the same Number value as y, return true.
      4. If x is +0 and y is −0, return true.
      5. If x is −0 and y is +0, return true.
      6. Return false.
    4. 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.
    5. If Type(x) is Boolean, return true if x and y are both true or both false. Otherwise, return false.
    6. Return true if x and y refer to the same object. Otherwise, return false.
  2. If x is null and y is undefined, return true.
  3. If x is undefined and y is null, return true.
  4. If Type(x) is Number and Type(y) is String, return the result of the comparison x == ToNumber(y).
  5. If Type(x) is String and Type(y) is Number, return the result of the comparison ToNumber(x) == y.
  6. If Type(x) is Boolean, return the result of the comparison ToNumber(x) == y.
  7. If Type(y) is Boolean, return the result of the comparison x == ToNumber(y).
  8. If Type(x) is either String or Number and Type(y) is Object, return the result of the comparison x == ToPrimitive(y).
  9. If Type(x) is Object and Type(y) is either String or Number, return the result of the comparison ToPrimitive(x) == y.
  10. Return false.

9.1 ToPrimitive

Table 10 — ToPrimitive Conversions

Input TypeResult
UndefinedThe result equals the input argument (no conversion).
NullThe result equals the input argument (no conversion).
BooleanThe result equals the input argument (no conversion).
NumberThe result equals the input argument (no conversion).
StringThe result equals the input argument (no conversion).
ObjectReturn 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,详细历程以下:

  1. 挪用对象的 valueOf() 要领,假如值是原值则返回
  2. 不然,挪用对象的 toString() 要领,假如值是原值则返回
  3. 不然,抛出 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');
}

浏览更多

    原文作者:离独逸
    原文地址: https://segmentfault.com/a/1190000014171155
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞