[译] 在JS中,如何让(a===1 && a===2 && a === 3)(严格相等)的值为true?

栏目: JavaScript · 发布时间: 5年前

内容简介:我们先简单了解这道JS经典问题, 然后再解决它的扩展问题。内容概览:如果你已经了解过这个问题并且知道如何解决这个JS谜题(是的,只是一个谜题,我并不想在生产代码中看到这样的用例) , 那你可以直接跳到下一节,阅读它的扩展问题。关于这个问题在reddit上有相关讨论, 下面是我看到最有趣的评论

我们先简单了解这道JS经典问题, 然后再解决它的扩展问题。

内容概览:

  • 重温(a==1&&a==2&&a==3)(宽松相等)问题
    • (a==1&&a==2&&a==3)问题描述
  • (a===1&&a===2&&a===3)扩展问题

如果你已经了解过这个问题并且知道如何解决这个JS谜题(是的,只是一个谜题,我并不想在生产代码中看到这样的用例) , 那你可以直接跳到下一节,阅读它的扩展问题。关于这个问题在reddit上有相关讨论, 下面是我看到最有趣的评论

"如果我在代码库中看到这样的代码,我可能就很绝望了" // 译者注: 谁看到都会很绝望吧

(a ==1 && a==2 && a==3) 的值可以是true吗?

回答是肯定的, 具体可以看下面的代码

const a = { value : 0 };
a.valueOf = function() {
    return this.value += 1;
};

console.log(a==1 && a==2 && a==3); //true
复制代码

通常, 在面试中问这类问题的目的并不是要求面试者记住这样的答案,而是想要了解面试者在面对这道题目时,是如何思考的以及他们是否有了解过Javascript中关于 == 的奇特的语法特性。

秘密就在于"宽松相等操作符 == "

在JS中,宽松相等 == 会先将左右两两边的值转化成相同的原始类型,然后再去比较他们是否相等。在转化之后( == 一边或两边都需要转化),最后的相等匹配会像 === 符号一样去执行判断。宽松相等是可逆的,对于任何值A与B,通常 A == BB == A 具有相同的表现(除了转换的顺序不同)。可以在这里详细深度地了解宽松匹配 == 与严格匹配 ===

Javascript会如何强制转换这个值呢?

在进行两个值的比较时,执行了类型的强制转换, 让我们先了解下内置的转换函数。

ToPrimitive(input, PreferredType?)
复制代码

可选参数 PreferredType 可以指定最终转化的类型,它可以是 Number 类型或 String 类型,这依赖于 ToPrimitive() 方法执行的结果返回的是 Number 类型或 String 类型。

值的转化过程如下

TypeError

如果 PreferredTypeNumber , 那转换算法就会像上述说明的顺序执行,如果是 String ,步骤2和步骤3会交换顺序。 PreferredType 是一个缺省值,如果不输入的话, Date 类型会被当作 String 类型处理,其他变量会当作 Number 处理。默认的 valueOf 返回 this ,默认的toString()会返回类型信息。

如上是操作符 +== 调用 toPrimitive() 的执行过程。

所以在上面的代码中, 如JS引擎所解析的, a == 11 是基本类型, JS引擎会尝试将 a 转换成 Number 类型,然后在上面的算法中, a.valueOf 被调用并且返回1(自增1并且返回自己)。在 a==2a==3 发生了同样的类型转换并增加自己的值。

(a === 1 && a === 2 && a ===3)的值也能是true吗?

当然也可以, 具体请看下面的代码

var value = 0; //window.value
Object.defineProperty(window, 'a', {
    get: function() {
        return this.value += 1;
    }
});

console.log(a===1 && a===2 && a===3) // true
复制代码

从经典问题的解答中,我们了解到JS中的原始类型将不再满足于上面的条件(严格相等没有转化的过程),所以我们需要通过一些方式去调用一个函数,并在这个函数中做我们想做的事情。但是执行函数往往需要在函数名字后引入 () 。并且由于这里不是宽松相等 ==valueOf 将不会被JS引擎调用。Emmm, 有点棘手。还好有 Property 函数, 特别是 getter 描述符, 带来了解决这个问题的办法。

属性描述符有两种类型, 数据描述符和存取描述符。

  1. 数据描述符

    强制键值 - value

    可选键值

    - configurable
     - enumable
     - writeable
    复制代码

    例子

    {
        value: 5,
        writable: true
    }
    复制代码
  2. 存取描述符

    强制键值 - get/set或都设定 可选键值 - confiturable - enumerable 例子

    {
        get: function () { return 5; },
        enumerable: true
    }
    复制代码

MDN上关于存取描述符的例子

// Example of an object property added
    // with defineProperty with an accessor property descriptor

    var bValue = 38;

    Object.defineProperty(o, 'b', {
        // Using shorthand method names (ES2015 feature).
        // This is equivalent to:
        // get: function() { return bValue; },
        // set: function(newValue) { bValue = newValue; },
        get() { return bValue; },
        set(newValue) { bValue = newValue; },
        enumerable: true,
        configurable: true
    });
    o.b; // 38
    // 'b' property exists in the o object and its value is 38
    // The value of o.b is now always identical to bValue,
    // unless o.b is redefined
复制代码

在问题的解决方案中, 我们使用 Object.defineProperty 为对象定义了一个属性。你可以在这里深入了解 Object.defineProperty 的语法与定义。 有趣的是, getset 是可以通过 "."操作符 调用的方法, 举个例子, a 有一个具有 getterb 属性, 它可以像对象的其他属性一样去调用,类似于 a.b 。这可以解决我们最初的问题, 我们需要调用一个无需 () 的函数, 通过 get 属性, 我们可以调用一个函数并且不用在函数名后添加 ()

在上面提到的解决方案中, 我们在window对象上定义了一个具有getter的 a 属性, 所以 a 可以在代码中直接被访问到(全局变量), 因此也可以直接获得a的值。如果我们在其他对象上定义了属性 a 而不是window的话,例如object1, 我们就需要改变题目为 object1.a===1 && object1.a===2 && object1.a===3 了。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

HTML5和CSS3实例教程

HTML5和CSS3实例教程

Brian P.Hogan / 李杰、刘晓娜、柳靖、朱嵬 / 人民邮电出版社 / 2012-1 / 39.00元

《HTML5和CSS3实例教程》共分3部分,集中讨论了HTML5和CSS3规范及其技术的使用方法。首先是规范概述,介绍了新的结构化标签、表单域及其功能(包括自动聚焦功能和占位文本)和CSS3的新选择器。接下来是HTML对视频和音频的支持,讲述了画布上的图形绘制及CSS阴影、渐变和变换的使用方法。最后介绍使用HTML5的客户端特性(包括WebStorage、WebSQLDatabases以及离线支持......一起来看看 《HTML5和CSS3实例教程》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具