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

更新日期: 2021-06-26阅读: 1.3k标签: 面试

这是一道面试题,大家先自己想一下,在什么情况下这个判断会成立?按正常思维想,这个是不可能成立的。

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

如果需要详细了解Object.defineProperty(),点击连接https://www.fly63.com/article/detial/4859


方案五:使用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

如果需要详细了解ES6 Proxy,点击连接http://www.fly63.com/article/detial/3341文章详细介绍了ES6 Proxy与数据劫持相关内容。


方案六:使用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.js
const 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.js
self.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.js
self.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。

在这里我们需要注意,一般情况下,通过WorkerpostMessage传递的数据要么是可以由结构化克隆算法处理的值(这种情况下是值的复制),要么是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,就退出循环。


链接: https://www.fly63.com/article/detial/10472

Web前端年后跳槽面试复习指南

很多童鞋可能年后有自己的一些计划,比如换份工作环境,比如对职业目标有了新的打算。当然面试这一关不得不过,大概又不可能系统性的复习,这里罗列一些 重点 面试的知识点和文章,

前端面试之webpack面试常见问题

什么是webpack和grunt和gulp有什么不同?什么是bundle,什么是chunk,什么是module?什么是Loader?什么是Plugin?如何可以自动生成webpack配置?webpack-dev-server和http服务器如nginx有什么区别?

每个 JavaScript 工程师都应当知道的 10 个面试题

多问问应聘者高层次的知识点,如果能讲清楚这些概念,就说明即使应聘者没怎么接触过 JavaScript,也能够在短短几个星期之内就把语言细节和语法之类的东西弄清楚。

37个JavaScript基本面试问题和解答

面试比棘手的技术问题要多,这篇文章整理了37个JavaScript基本面试问题和解答,这些仅仅是作为指导。希望对前端开发的你有所帮助!

React常见面试题

React常见面试题:React中调用setState之后发生了什么事情?React中Element与Component的区别?优先选择使用ClassComponent而不是FunctionalComponent?React中的refs属性的作用是什么?React中keys的作用是什么?

有趣的Js面试题_如何让 (a == 1 && a == 2 && a == 3) 返回 true

题目大意为:JS 环境下,如何让 a == 1 && a == 2 && a == 3 这个表达式返回 true ?这道题目乍看之下似乎不太可能,因为在正常情况下,一个变量的值如果没有手动修改,在一个表达式中是不会变化的。

js练习笔记:10道JavaScript题目

10道JavaScript题目:累加函数addNum、实现一个Person类、实现一个arrMerge 函数、实现一个toCamelStyle函数、setTimeout实现重复调用、实现一个bind函数、实现一个Utils模块、输出一个对象自身的属性

vue菜鸟从业记:没准备好的面试,那叫尬聊

面试开场白总缺少不了自我介绍,一方面是面试官想听听你对自己的介绍,顺便有时间看看简历上的描述,是否与口述一致。另一方面就是看看你简历上做过什么项目,用到了哪些技术栈,一会儿好提问你。

毕业一年左右的前端妹子面试总结

把面试当做学习,这个过程你会收益很大。前端知识很杂,可能实际工作中用到的技术,像框架都是跟着公司的要求走的,像我最近也在看React啦,Vue和React都对比着再学习

vue面试时需要准备的知识点

vue上手可以说是比较轻松而且简单,如果你用过angular,react,你也会很喜欢vue。vue的核心思想依旧是:构建用户界面的渐进式框架,关注视图的变化。这也是为什么新建的文件是结构是template script style

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!