本文共 4901 字,大约阅读时间需要 16 分钟。
这是一道面试题,大家先自己想一下,在什么情况下这个判断会成立?按正常思维想,这个是不可能成立的。
if(a == 1 && a == 2 && a == 3){ console.log(true);}//console.log(a == 1 && a == 2 && a == 3); // true
怎么实现a 能与1,2,3相等呢?下面我整理一些可行的实现方案,希望对你有所帮助。
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对象、对象中的属性,属性描述符。属性描述符中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用于在目标对象的外层搭建了一层拦截,外界对目标对象的某些操作,必须通过这层拦截。我们这里重新定义了属性的读取(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定义一个全局的属性_a,当属性_a被访问的时候就会调用上面定义的getter方法,所以和上面对象的隐式类型转换过程是一样的。
let _a= 1;Reflect.defineProperty(this, 'a', { get() { return _a++; }});console.log(a === 1 && a === 2 && a === 3);//true
这是在底层的内存上修改一个变量的值,而不是通过一些所谓的技巧去让上面的表达式成立。而且这在现实的开发中是可能会出现的一种情况。在进入下面的讲解之前,我们需要先了解一些前置的知识点。
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,就退出循环。
本文完~
学习更多技能
请点击下方公众号
转载地址:http://pwdpi.baihongyu.com/