当我第一次看到这一题目的时候,我是比较震惊的,分析了下很不合我们编程的常理,并认为不大可能,变量a要在同一情况下要同时等于1,2和3这三个值,这是天方夜谭吧,不亚于哥德巴赫1+1=1的猜想吧,不过一切皆有可能,出于好奇心,想了许久之后我还是决定尝试解决的办法。

我的思路来源于更早前遇到的另外一题相似的面试题:

// 设置一个函数输出一下的值
f(1) = 1;
f(1)(2) = 3;
f(1)(2)(3) = 6;

当时的解决办法是使用toString或者valueOf实现的,那我们先回顾下toStringvalueOf方法,方便我们更深入去了解这类型的问题:

比如我们有一个对象,在不重写toString()方法和valueOf()方法的情况下,在 Node 或者浏览器输出的结果是这样的

class Person {
constructor() {
this.name = name;
}
} const best = new Person("Kobe");
console.log(best); // log: Person {name: "Kobe"}
console.log(best.toString()); // log: [object Object]
console.log(best.valueOf()); // log: Person {name: "Kobe"}
console.log(best + "GiGi"); // log: [object Object]GiGi
best Person
best.toString() [object Object]
best.valueOf() Person
best + 'GiGi' [object Object]GiGi

从上面的输出我们可以观察到一个细节,toString()输出的是[object Object],而valueOf()输出的是Person对象本身,而当运算到best + 'GiGi'的时候竟然是输出了[object Object]GiGi,我们可以初步推断是对象调用的toString()方法得到的字符串进行计算的,难道是运算符+的鬼斧神工吗?

为了验证我们上一步的推断,我们稍微做一点改变,把 valueOf 方法进行一次复写:

class Person {
constructor(name) {
this.name = name;
}
// 复写 valueOf 方法
valueOf() {
return this.name;
}
}
best Person
best.toString() [object Object]
best.valueOf() Person
best + 'GiGi' KobeGiGi

这次跟上面只有一处产生了不一样的结果,那就是最后的best + 'GiGi'前后两次结果在复写了valueOf()方法之后发生了改变,从中我们可以看出来,对象的本质其实没有发生根本的改变,但是当它被用作直接运算的时候,它的值是从复写的valueOf()中获取的,并继续参与后续的运算。

当然不要忘了我们还有个toString()方法,所以我们也复写它,看看结果会不会也受影响:

class Person {
constructor(name) {
this.name = name;
}
valueOf() {
return this.name;
}
toString() {
return `Bye ${this.name}`;
}
}
best Person
best.toString() Bye Kobe
best.valueOf() Kobe
best + 'GiGi' KobeGiGi

我们发现 best + 'GiGi'还是没有发生任何改变,还是使用我们上一次复写valueOf()的结果

其实我们重写了valueOf方法,不是一定调用valueOf()的返回值进行计算的。而是valueOf返回的值是基本数据类型时才会按照此值进行计算,如果不是基本数据类型,则将使用toString()方法返回的值进行计算。

class Person {
constructor(name) {
this.name = name;
}
valueOf() {
return this.name;
}
toString() {
return `Bye ${this.name}`;
}
}
const best = new Person({ name: "Kobe" }); console.log(best); // log: Person name: {name: "Kobe"}
console.log(best.toString()); // log: Bye [object Object]
console.log(best.valueOf()); // log: Person {name: "Kobe"}
console.log(best + "GiGi"); // log: [object Object]GiGi
best Person
best.toString() Bye [object Object]
best.valueOf() {name: "Kobe"}
best + 'GiGi' Bye [object Object]GiGi

看上面的例子,现在传入的name是一个对象new Person({ name: "Kobe" }),并不是基本数据类型,所以当执行加法运算的时候取toString()方法返回的值进行计算,当然如果没有valueOf()方法,就会去执行toString()方法。

所以铺垫了这么久,我们就要揭开答案,我们正是使用上面这些原理去解答这一题:

class A {
constructor(value) {
this.value = value;
}
toString() {
return this.value++;
}
}
const a = new A(1);
if (a == 1 && a == 2 && a == 3) {
console.log("Hi Eno!");
}

这里就比较简单,直接改写toString()方法,由于没有valueOf(),当他做运算判断a == 1的时候会执行toString()的结果。

class A {
constructor(value) {
this.value = value;
}
valueOf() {
return this.value++;
}
}
const a = new A(1);
if (a == 1 && a == 2 && a == 3) {
console.log("Hi Eno!");
}

当然,你也可以不使用toString,换成valueOf也行,效果也是一样的:

class A {
constructor(value) {
this.value = value;
}
valueOf() {
return this.value++;
}
} const a = new A(1);
console.log(a);
if (a == 1 && a == 2 && a == 3) {
console.log("Hi Eno!");
}

所以,当一个对象在做运算的时候(比如加减乘除,判断相等)时候,往往会有valueOf()或者toString的调用问题,这个对象的变量背后通常隐藏着一个函数。

当然下面这题原理其实也是一样的,附上解法:

// 设置一个函数输出一下的值
f(1) = 1;
f(1)(2) = 3;
f(1)(2)(3) = 6; function f() {
let args = [...arguments];
let add = function() {
args.push(...arguments);
return add;
};
add.toString = function() {
return args.reduce((a, b) => {
return a + b;
});
};
return add;
}
console.log(f(1)(2)(3)); // 6

当然还没有结束,这里还会有一些特别的解法,其实在使用对象的时候,如果对象是一个数组的话,那么上面的逻辑还是会成立,但此时的toString()会变成隐式调用join()方法,换句话说,对象中如果是数组,当你不重写其它的toString()方法,其默认实现就是调用数组的join()方法返回值作为toString()的返回值,所以这题又多了一个新的解法,就是在不复写toString()的前提下,复写join()方法,把它变成shift()方法,它能让数组的第一个元素从其中删除,并返回第一个元素的值。

class A extends Array {
join = this.shift;
}
const a = new A(1, 2, 3);
if (a == 1 && a == 2 && a == 3) {
console.log("Hi Eno!");
}

我们的探寻之路还没结束,细心的同学会发现我们题目是如何让(a===1&&a===2&&a===3)的值为 true,但是上面都是讨论宽松相等==的情况,在严格相等===的情况下,上面的结果会不同吗?

答案是不一样的,你们可以试试把刚才上面的宽松条件改成严格调试再试一次就知道结果了。

class A extends Array {
join = this.shift;
}
const a = new A(1, 2, 3);
// == 改成 === 后:
if (a === 1 && a === 2 && a === 3) {
console.log("Hi Eno!"); // Hi Eno!此时再也没出现过了
}

那么此时的情况又要怎么去解决呢?我们可以考虑一下使用Object.defineProperty来解决,这个因为Vue而被众人熟知的方法,也是现在面试中一个老生常谈的知识点了,我们可以使用它来劫持a变量,当我们获取它的值得时候让它自增,那么问题就可以迎刃而解了:

var value = 1;
Object.defineProperty(window, "a", {
get() {
return this.value++;
}
}); if (a === 1 && a === 2 && a === 3) {
console.log("Hi Eno!");
}

上面我们就是劫持全局window上面的a,当a每一次做判断的时候都会触发get属性获取值,并且每一次获取值都会触发一次函数实行一次自增,判断三次就自增三次,所以最后会让公式成立。

当然这里还有其他方法,这里再举例一个,比如使用隐藏字符去做障眼法瞒过面试官的:

var aᅠ = 1;
var a = 2;
var ᅠa = 3;
if (aᅠ == 1 && a == 2 && ᅠa == 3) {
console.log("Hi Eno!");
}

上面这种解法的迷惑性很强,如果不细心会以为是三个一样的a,其实本质上是定义三个不一样的a值,a的前后都有隐藏的字符,所以调试的时候,请复制粘贴上面的代码调试,自己在Chrome手打的话可以用特殊手段让 a 后面放一个或者两个红点实现,并在回车的时候,调试工具会把这些痕迹给隐藏,从而瞒天过海,秀到一时半刻还没反应过来的面试官。

最后,祝愿大家在新的一年找到一份如意的工作,上面的代码在实际情况中基本是不会被运用到的,但是用来探索JS的无限可能是具有启发性的,也建议面试官不要使用这类面试题去难为面试者~

如果文章和笔记能带您一丝帮助或者启发,请不要吝啬你的赞和收藏,你的肯定是我前进的最大动力

附笔记链接,阅读往期更多优质文章可移步查看,喜欢的可以给我点赞鼓励哦:

大厂面试题分享:如何让(a===1&&a===2&&a===3)的值为true?的更多相关文章

  1. BATJ等大厂最全经典面试题分享

    金九银十,又到了面试求职高峰期,最近有很多网友都在求大厂面试题.正好我之前电脑里面有这方面的整理,于是就发上来分享给大家. 这些题目是网友去百度.蚂蚁金服.小米.乐视.美团.58.猎豹.360.新浪. ...

  2. 就目前市面上的面试整理来说,最全的BAT大厂面试题整理在这

    原标题:就目前市面上的面试整理来说,最全的BAT大厂面试题整理在这 又到了面试求职高峰期,最近有很多网友都在求大厂面试题.正好我之前电脑里面有这方面的整理,于是就发上来分享给大家. 这些题目是网友去百 ...

  3. 双非本科字节跳动Android面试题分享(已拿offer)

    基本情况 本人系非985非211普通本科生一枚,有实习有项目经历但成绩普通,在面试前刷了很多面经.面试题,这里也把自己的分享下,做个回报好了,顺便攒攒人品,一到这种时候人就迷信起来了. 面试是以视频面 ...

  4. 多次面试被拒,‘宅家苦修’30天,终获美团offer(含字节跳动/阿里/腾讯等大厂面试题整理)

    背景:双非渣本. 今年由于疫情,上半年一直在家里.2月份本来无忧无虑,呆在家里不给国家添乱的时候,发现身边的同学找到了大厂的offer.心里开始有点慌张.本来想在3月份如果能回到学校,就开始考研之路, ...

  5. 350道面试题分享,拿下京东offer工资double

    350道面试题分享,拿下京东offer工资double 前言: 面试,其实是一个双向选择的过程,在这个过程里,我们不应该抱着畏惧的心态去对待,这样反而会影响自己的发挥.同时看中的应该不止薪资,还要看你 ...

  6. 深圳-2020-java面试题分享

    记录一下最近面试接触的面试题. 深圳掌众传媒: union 和union all区别 union:对两个结果集进行并集操作,不包括重复行,同时进行默认规则的排序: union All:对两个结果集进行 ...

  7. 【剑指Offer面试题】 九度OJ1368:二叉树中和为某一值的路径

    题目链接地址: http://ac.jobdu.com/problem.php? pid=1368 题目1368:二叉树中和为某一值的路径 时间限制:1 秒内存限制:32 兆特殊判题:否提交:2252 ...

  8. 秋招已过,各大厂的面试题分享一波 附C++实现

    数据结构和算法是面试的一座大山,尤其去面试大厂更是必不可少!简单说明一下为啥喜欢考数据结构和算法,首先,算法有用也没用,如果是中小型企业的简单业务逻辑,可能用不到啥算法,但大厂一定会用到,都知道数据库 ...

  9. <面试题分享> 记两次58面试

    说明 来北京找工作,有个猎头看我的简历不错,帮我投了两个58同城的面试,投的都比较高,题也注重原理,较难,这里分享出来,给有需要的人和自己提个醒,保持空杯 面试题内容 2019.05.07 北京58企 ...

随机推荐

  1. 如何将 Ubuntu 版本升级到新版本

    @ 目录 0.将 Ubuntu 版本升级到新版本的注意事项 1.以图形方式升级到 Ubuntu 20.04(适用于桌面用户) 2.使用命令行升级到 Ubuntu 21.10 本教程通过从 Ubuntu ...

  2. 创建一个People类型,有年龄、工资、性别三个属性。 定义一个方法叫做找对象,找对象方法传过来一个人;

    创建一个People类型,有年龄.工资.性别三个属性. 定义一个方法叫做找对象,找对象方法传过来一个人: 首先如果性别相同,就输出"我不是同性恋", 如果对方是男的,年龄小于28, ...

  3. ❤️❤️用最简单的方法在Webstorm中打开已存在项目 和 新建Vue项目 (亲测实用)❤️❤️

    ​ 目录 一:打开已存在项目时 二:新建一个vue项目 使用webstorm创建vue项目创建vue项目各个公司用的工具都不一样 最常见的有HBuilder X,WebStorm,Visual Stu ...

  4. 双非Java的学习之旅以及秋招路程

    个人信息: 趁着中秋写个帖子记录一下吧.渣渣本,无实习,无高质量证书,走了很多弯路,最后选择的Java后端.现在算是半躺平了,接了几个中小厂的offer保底,20w多的薪资,后面还有几家公司接着面.不 ...

  5. vue报错 Uncaught TypeError: Cannot read property of null

    有可能是点击a标签,但是a标签有click事件,未阻止默认事件导致报错,开始都看不出来是什么错误

  6. 使用fiddler抓包模拟器及配置fiddler过滤

    一. 安装fiddler https://www.telerik.com/fiddler 二. 配置fiddler,一下的ip要根据自己电脑情况设置 然后重启Fiddler,一定要重启!!! 三.配置 ...

  7. java面向对象编程(上)

    java面向对象学习的三条主线 1.Java类及类的成员:属性.方法.构造器.代码块.内部类 2.面向对象的三大特征:封装性.继承性.多态性.(抽象性) 3.其它关键字:this.super.stat ...

  8. django使用restframework序列化查询集合(querryset)

    第一: pip install djangorestframework 第二: 在setting.py文件中的app添加名为: 'rest_framework', 第三:再项目的APP下面新建名为(可 ...

  9. sunny 内网穿透使用。

    启动方法:

  10. HTML 网页开发、CSS 基础语法——九.CSS概述

    1.产生背景 从HTML的答案盛开时,样式就以各种形式存在,最初的HTML只i包含很少的显示属性.随着HTML的成长为了满足页面设计者的要求,HTML添加了许多显示功能,随着功能的增加HTML页面变得 ...