资讯专栏INFORMATION COLUMN

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

BDEEFE / 587人阅读

摘要:稍后我们再详细剖析,接下来先看一个问题。还内建了一些在之前没有暴露给开发者的,它们代表了内部语言行为。使用,可能有不少朋友一开始就想到这种方式,简单贴一下阅读更多

在 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");
}
阅读更多

Can (a== 1 && a ==2 && a==3) ever evaluate to true?

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/93950.html

相关文章

  • 魔幻语言 JavaScript 系列类型转换、宽松相等以及原始值

    摘要:通过使用其构造函数,可以将一个值的类型转换为另一种类型。如果使用两次,可用于将该值转换为相应的布尔值。 编译自:[1] + [2] – [3] === 9!? Looking into assembly code of coercion.全文从两个题目来介绍类型转换、宽松相等以及原始值的概念: [1] + [2] – [3] === 9 如果让 a == true && a == fa...

    li21 评论0 收藏0
  • 魔幻语言 JavaScript 系列 call、bind 以及上下文

    摘要:那么,它到底是如何工作的呢让我们从一种更简单的实现开始实际上这种实现代码更短,并且更易读是函数原型中的一个函数,它调用函数,使用第一个参数作为参数,并传递剩余参数作为被调用函数的参数。 原文:The Most Clever Line of JavaScript 作者:Seva Zaikov 原文 最近 一个朋友 发给我一段非常有趣的 JavaScript 代码,是他在某个 开源库中...

    cuieney 评论0 收藏0
  • JS系列call & apply & bind

    摘要:参考链接在中,和是对象自带的三个方法,都是为了改变函数体内部的指向。返回值是函数方法不会立即执行,而是返回一个改变了上下文后的函数。而原函数中的并没有被改变,依旧指向全局对象。原因是,在中,多次是无效的。 参考链接:https://juejin.im/post/59bfe8... 在JavaScript中,call、apply和bind是Function对象自带的三个方法,都是为了改变...

    xiaochao 评论0 收藏0
  • javascript中的constructor&&prototype

    摘要:于是退而求其次叫为类的构造函数。如果这个函数被用在创建自定义对象的场景中,我们称这个函数为构造函数。遇到的问题始终指向创建当前对象的构造函数。 Object.constructor,prototype 对象的prototype和constructor是两个重要的属性,他们总是成对出现,提到constructor的地方,不得不涉及到另外一个非常重要的属性prototype,它是js中基于...

    huaixiaoz 评论0 收藏0
  • 前端跨域原因&&方案&&原理

    摘要:于是乎同源策略应运而生主要限制在于和无法读取。怎么绕过同源策略首先一般来说协议和端口造成的跨域问题大部分方法是没有办法绕过的。二级域名是寄存在主域名之下的域名。当主域名受到惩罚二级域名也会连带惩罚。 前言 这是一道前端跨不过躲不掉面试必备的知识,挣扎多年没能做到刻骨铭心深入脊髓,只能好好写篇博文记录起来了; 什么是跨域? 广义来说,A域执行的文档脚本试图去请求B域下的资源是不被允许的,...

    Zack 评论0 收藏0

发表评论

0条评论

BDEEFE

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<