这算得上是近些年的前端网红题了,曾经对这种网红题非常抵触,认为非常没有意义。
看到了不少人有做分享,有各种各样的方案,有涉及到 JS 非常基础的知识点,也不得不感叹解题者的脑洞之大。
但是,拿来做面试题为难没有看过的面试者,就非常非常不地道了。
鹤顶红是武侠剧中出现最多的毒药,真的是江湖出行必备的毒药。
正常情况下,如果?let a = 1;
?则,表达式的后半段必不成立。但是容易想到如果在每个阶段自增后,表达式是可能成立的。
let a = 1;
a++ === 1 && a++ === 2 && a++ === 3; // true
可是这就属于把题改了,我们需要把自增的逻辑藏起来。由于?a
?是基础类型,我们基本上不可能对其做什么改造了。
但是我们如果把?a
?定义到?window
?上,再加上诸如?Object.defineProperty
?的 api,是能够做到的。
let tmp = 1;
Object.defineProperty(window, 'a', {
get: function () {
return tmp++;
},
})
a === 1 && a === 2 && a === 3; // true
由于?defineProperty
?的限制,我们不能把?1
?作为?value
, 设置在属性描述符号内部,不得不外部定义一个变量。
defineProperty
?在诸如 Vue2 的框架中,用的非常多,几乎是面试高频会被问到的,所以这种“毒药”是你行走前端江湖必备的。
曼陀罗这个名字颇具异域风情、《神雕侠侣》中杨过就是中了此毒。
说到这里我是要将此题改为另一道类似的题目:使?a == 1 && a == 2 && a == 3
?返回?true
。
将严格相等改为了普通的相等,因为通常代码规范中要求使用严格相等,导致?==
?不常写,故将此命名为这个异域的名字。
正因为是不严格相等,我们可以利用 JS 中的类型转换机制来下毒。
这个机制是这样的:
我们可以利用最后一点解这个题:
const a = {
value: 1,
valueOf: function () {
return this.value++;
},
}
a == 1 && a == 2 && a == 3;
先前提到的那种解法必然也可以解这道题,但是这个写法不能用于上一道题,因为全等不会触发类型转换。
此毒无色无香,药性一发作便全身筋骨酸软,数日后虽行动如常,内力半点发挥不出。毒药和解药表面无异,若中毒者再服毒药则气绝身亡。
const a? = 1;
const a? = 2;
const a = 3;
a? === 1 && a? === 2 && a === 3
你可能会说:“这段代码应该是会报错的!”。但是相信我,你复制到控制台执行一下试试。注意我这里有一个?a
?是正常的?a
,如果你有执行过上面别的方法,注意先刷新一下窗口。
是的?const
?声明的是常量,理应抛出错误:Identifier 'haha' has already been declared
。
实际上,我这里定义的三个?a
?是不同的变量,有一个是?a
, 一个是?ax
, 一个是?ay
,其中 x, y 是被我替换为 unicode 里不可见的字符,所以在上面的代码里看到的都是?a
?的形式。
我目前正在使用 webstorm 编辑这篇文章,我也发现了这些不可见字符会在编辑器里显示出来“怪怪的”,如果你也打开这篇 markdown,你会发现:
构造变量名称(唯一标识符)的通用规则是:
实际上,使用中文,甚至是 emoji 表情作为变量名称都是合法的,所以这里可以使用不可见的 unicode 字符作为变量名。
当然,如果想要运行正确,我们得偷偷的把题改了,使用我们新声明的变量,虽然它们看起来一摸一样。
这种“下毒”,真是无色无味,正像软筋散一样。
这边汇总了二个针对原题的解法:通过使用?Object.defineProperty
?和利用变量名称的规则的方法;
以及对它的变种题目:通过对象类型转化时默认调用?valueOf
?函数的机制。
这道题至少也算给我们带来了三个 JS 基础知识点,好了,你(这道题)可以毒发身亡了。
地址:前端面试题库