如何实现a===1 && a===2 && a===3返回true?

web前端开发

共 5269字,需浏览 11分钟

 · 2021-07-05

这是一道面试题,大家先自己想一下,在什么情况下这个判断会成立?按正常思维想,这个是不可能成立的。
if(a == 1 && a == 2 && a == 3){   console.log(true);}//console.log(a == 1 && a == 2 && a == 3); // true

怎么实现a 能与1,2,3相等呢?下面我整理一些可行的实现方案,希望对你有所帮助。

方案一:重写toString()或valueOf()

let a = {  i: 1,  toString: function () {    return a.i++;  }}console.log(a == 1 && a == 2 && a == 3); // true

在做判断时,首先会调用valueOf函数,数组调用valueOf后返回的还是数组本身,就会再次调用toString函数,这里是重写了toString方法,并且return的是a.i++,所以每调用一次,都会在上次的值得基础上自加一次,
所以结果为true。同下:

let a = {    i: 1,    valueOf: function()  {        return this.i++;    }}console.log(a == 1 && a == 2 && a == 3); // true

这里i定义为1,我们还可以定义正则表达式的方式来实现,比如:

let a = {   reg: /\d/g,   valueOf: function() {      return this.reg.exec(123)[0];   },};console.log(a == 1 && a == 2 && a == 3); // true

方案二:数组

数组的toString接口默认调用数组的join方法,重写join方法。定义a为数字,每次比较时就会调用 toString()方法,我们把数组的shift方法覆盖toString即可:

let a = [1,2,3];a.toString = a.shift;console.log(a == 1 && a == 2 && a == 3); // true

当然把toString改为valueOf也是一样效果:

let a = [1,2,3];a. valueOf  = a.shift;console.log(a == 1 && a == 2 && a == 3); // true

结合方案一,我们还可以这样来实现:

let a = {   value:[3,2,1],   valueOf:function() {  //或者toString         return this.value.pop();     },}console.log(a == 1 && a == 2 && a == 3); // true

方案三:数字变量

我们将数字作为变量名,让a=1,让数字变量=a。

let a = 1;let 1 = a;let 2 = a ;let 3 = a ;console.log( a ==ᅠ1 && a ==ᅠ2 && a ==ᅠ3 ); // true

方案四:使用Object.defineProperty()

Object.defineProperty()用于定义对象中的属性,接收三个参数:object对象、对象中的属性,属性描述符。属性描述符中get:访问该属性时自动调用,vue3之前的版本实现双向绑定主要就用到了它。

Object.defineProperty(this, 'a', {    get: function () {        return this.value = this.value ? (this.value += 1) : 1    }})console.log(a===1 && a===2 && a===3) //true

或者

var  _a = 1;Object.defineProperty(this,'a',{  get:function(){    return _a++  }})console.log(a===1 && a===2 && a===3)//true

方案五:使用ES6 Proxy

es6的proxy用于在目标对象的外层搭建了一层拦截,外界对目标对象的某些操作,必须通过这层拦截。我们这里重新定义了属性的读取(get)行为。

let a = new Proxy({ i: 0 }, {    get: (target, name) => name === Symbol.toPrimitive ? () => ++target.i : target[name],});console.log(a == 1 && a == 2 && a == 3);    // true

方案六:使用Reflect.defineProperty

我们还可以通过Reflect.defineProperty定义一个全局的属性_a,当属性_a被访问的时候就会调用上面定义的getter方法,所以和上面对象的隐式类型转换过程是一样的。

let _a= 1;Reflect.defineProperty(this, 'a', {    get() {        return _a++;    }});console.log(a === 1 && a === 2 && a === 3);//true

方案七:Race Condition(竞态条件)

这是在底层的内存上修改一个变量的值,而不是通过一些所谓的技巧去让上面的表达式成立。而且这在现实的开发中是可能会出现的一种情况。在进入下面的讲解之前,我们需要先了解一些前置的知识点。

SharedArrayBuffer

SharedArrayBuffer对象用来表示一个通用的,固定长度的原始二进制数据缓冲区,类似于 ArrayBuffer对象,它们都可以用来在共享内存上创建视图。与ArrayBuffer不同的是SharedArrayBuffer不能被分离。

Web Worker

Web Worker为Web内容在后台线程中运行脚本提供了一种简单的方法。线程可以执行任务而不干扰用户界面。

此外,他们可以使用XMLHttpRequest执行 I/O (尽管responseXML和channel属性总是为空)。

一旦创建, 一个worker 可以将消息发送到创建它的JavaScript代码, 通过将消息发布到该代码指定的事件处理程序(反之亦然)。详情可以参考使用 Web Workers。

了解了前置的知识我们直接看接下来的代码实现吧。

index.js

// index.jsconst worker = new Worker('./worker.js');const competitors = [   new Worker('./competitor.js'),   new Worker('./competitor.js'),];const sab = new SharedArrayBuffer(1);worker.postMessage(sab);competitors.forEach(w => {   w.postMessage(sab);});

worker.js

// worker.jsself.onmessage = ({ data }) => {   const arr = new Uint8Array(data);   Reflect.defineProperty(self, 'a', {      get() {         return arr[0];      },   });   let count = 0;   while (!(a === 1 && a === 2 && a === 3)) {      count++;      if (count % 1e8 === 0) console.log('running...');   }   console.log(`After ${count} times, a === 1 && a === 2 && a === 3 is true!`);};

competitor.js

// competitor.jsself.onmessage = ({ data }) => {   const arr = new Uint8Array(data);   setInterval(() => {      arr[0] = Math.floor(Math.random() * 3) + 1;   });};

在开始深入上面的代码之前,你可以在本地运行一下上面的代码,在看到结果之前可能需要等上一小会。

或者直接在这里打开浏览器的控制台看一下运行的结果。

需要注意的是,因为SharedArrayBuffer现在仅在Chrome浏览器中被支持,所以需要我们使用Chrome浏览器来运行这个程序。

运行之后你会在控制台看到类似如下的结果:

158 running...After 15838097593 times, a === 1 && a === 2 && a === 3 is true!

我们可以看到,运行了15838097593次才出现一次相等。不同的电脑运行这个程序所需要的时间是不一样的,就算同一台机器每次运行的结果也是不一样的。

下面我们来深入的讲解一下上面的代码,首先我们在index.js中创建了三个worker,其中一个worker用来进行获取a的值,并且一直循环进行比较。直到a === 1 && a === 2 && a === 3成立,才退出循环。

另外两个worker用来制造Race Condition,这两个worker一直在对同一个地址的数据进行修改。

在index.js中,我们使用SharedArrayBuffer申请了一个字节大小的一段连续的共享内存。

然后我们通过worker的postMessage方法将这个内存的地址传递给了3个worker。

在这里我们需要注意,一般情况下,通过Worker的postMessage传递的数据要么是可以由结构化克隆算法处理的值(这种情况下是值的复制),要么是Transferable类型的对象(这种情况下,一个对象的所有权被转移,在发送它的上下文中将变为不可用,并且只有在它被发送到的worker中可用)。

更多详细内容可以参考Worker.postMessage() 。

但是如果我们传递的对象是SharedArrayBuffer类型的对象,那么这个对象的代表的是一段共享的内存,是可以在主线程和接收这个对象的Worker中共享的。

在competitor.js中,我们获取到了传递过来的SharedArrayBuffer对象,因为我们不可以直接操作这段内存,需要在这段内存上创建一个视图,然后才能够对这段内存做处理。

我们使用Uint8Array创建了一个数组,然后设置了一个定时器一直对数组中的第一个元素进行赋值操作,赋值是随机的,可以是1,2,3中的任何一个值。

因为我们有两个worker同时在做这个操作,所以就形成了Race Condition。

在worker.js中,我们同样在传递过来的SharedArrayBuffer对象上创建了一个Uint8Array的视图。然后在全局定义了一个属性a,a的值是读取Uint8Array数组的第一个元素值。

然后是一个while循环,一直在对表达式a === 1 && a === 2 && a === 3进行求值,直到这个表达式的值为true,就退出循环。

本文完~

学习更多技能

请点击下方公众号



浏览 21
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报