大厂面试题分享:如何让(a===1&&a===2&&a===3)的值为true?菜鸟知识_面试使用教程

大厂面试题分享:如何让(a===1&&a===2&&a===3)的值为true?菜鸟知识

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

大厂面试题分享:如何让(a===1&&a===2&&a===3)的值为true?菜鸟知识_面试使用教程

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

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

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

比如我们有一个对象,在不重写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
bestPerson
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;
  }
}
bestPerson
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}`;
  }
}
bestPerson
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]10
bestPerson
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) = 2;
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的无限可能是具有启发性的,也建议面试官不要使用这类面试题去难为面试者~

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

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

海计划公众号
(0)
上一篇 2020/03/20 04:06
下一篇 2020/03/20 04:06

您可能感兴趣的内容

  • CSS盒子模型入门教程_模型入门攻略

    一、什么叫框模型页面元素皆为框(盒子),定义了元素框处理元素内容,内边距,外边距以及边框的计算方式。二、外边距围绕在元素边框外的空白距离(元素与元素之间的距离)语法:margin,定义4个方向的外边距1、单边定义:margin-top/right/bottom/left(1)取值:以px为单位, %占父级元素宽度的%比正数:margin-left 元素向右移

    2020/03/24
  • Screen Size入门知识_在线查询不同设备的屏幕尺寸

    Screen Size攻略教程 官方网址:http://screensiz.es/ 简介描述:在线查询不同设备的屏幕尺寸 Screensiz.es is a handy datab…

    2020/03/06
  • 控制预期,走向成功使用说明_活动新手入门

    长辈们是否常对您说:不要抱有太大期望,否则很容易失望。事实上,老人家的话很有道理。当您对自己的事业有很高的预期时,失败往往近在咫尺。一些成功导师希望您志存高远,但脚踏实地总是没错。管理客户预期是商务领域中的重要一环。我们许下小诺言,并争取带来大回报,这样才有利于建立坚不可摧的客户关系。在商务中我们对这一方面过于重视,常常导致我们忘记自己的期望,以及这些期望对

    2020/03/30
  • Fortune.js基础指南_一个超媒体 API 原型框架

    Fortune.js基础指南 官方网址:http://fortune.js.org GitHub:https://github.com/fortunejs/fortune 简介描述…

    2020/03/06
  • 性感与色情有多远小白基础你不知道的图片鉴黄那些事_图片小白知识

    图片鉴黄服务市场容量巨大,作为移动互联网行业最为热门的创业领域,移动社交类App每天生产大量图片,并有无数色情图片混杂其中,所以高效准确地鉴别和剔除淫秽色情信息成为一项十分艰巨的任务。此外,移动直播的大热也导致图片鉴黄需求大增,尤其对于中小开发团队而言,直播平台很可能因为人力监管问题而在涉黄审核方面出现风险。而自主研发鉴黄功能或增加审核人员又会增加产品和服务

    2020/04/05
  • 使用Rollup打包并发布到npm使用指南_Rollup小白入门

    前言其实用 webpack 也可以打包库,不过根据create-react-app项目贡献者的说法:rollup适合发布 js 库,而webpack更适合做应用程序。简而言之就是:rollup 打包 js 比 webpack 方便,而 webpack 打包 css 比 rollup 方便,相信大家已经有所了解了,至于选用哪个相信大家已经清楚了,下面我们来详细

    2020/03/29
  • Verdaccio小白基础_一个轻量级的私有npm代理注册表

    Verdaccio小白基础 官方网址:https://www.verdaccio.org/ GitHub:https://github.com/verdaccio/verdacci…

    2020/03/06
  • 彻底掌握css动画【transition】菜鸟指南_动画小白常识

    说起来css动画是一个很尬的事,一方面因为公司用css动画比较少,另一方面大部分开发者习惯了用JavaScript来做动画,所以就导致了许多程序员比较排斥来学习css动画(至少我是),但是一个不懂css动画的前端工程师不能称之为掌握css3,其实当你真正学习css动画之后,你会被它的魅力所吸引的,它可以减少代码量、提高性能。话不多说,马上和我一起去学习今天的

    2020/03/24
  • 解决React路由URL中hash(#)部分的显示 、browserHistory打包后浏览器刷新页面出现404的问题零基础入门_404攻略教程

    摘要在React项目中,我们需要采用它的路由库React-Router来进行页面跳转,React会根据路由URL来判断是哪个页面。常见的的URL有两种显示方式,一种是hashHistory的形式,形如:localhost:3000/#/free-lesson的路由,另一种是browserHistory的形式,形如:localhost:3000/person-

    2020/03/22
  • NGINX Ingress Controller 设计原理基础知识_原理入门百科

    原文 https://kubernetes.github.io/ingress-nginx/how-it-works/#nginx-modelingress controller 如何工作这篇文档的目的是解释 nginx ingress 控制器是如何工作的,特别是 nginx 模型的构建以及为什么需要它。Nginx 配置nginx ingress 控制器目标

    2020/03/20
  • 递归算法的理解基础知识教程_递归基础知识教程

    递归:所谓递归,就是既有传递,又有回归,与其说是传递与回归,初学不如理解是一种 “循序递进”与“规律约束”。为什么这样说,因为递归算法相比较于循环在代码结构方面个人认为更加简洁清晰,清晰易懂,递归注重的是一种有序的规律,所以在每个程序开始之前,我们只要能找到一个使程序循序递进的规律;并且在整个过程都在用此规律进行传递,但是递归算法也有很大的缺点,会造成内存

    2020/03/24
  • web前端开发书籍推荐新手入门css/css3的好书有哪些?_书籍菜鸟攻略

    css/css3样式已是web前端开发的主流技术了。每个优秀的前端程序员都应该熟悉,甚至精通css。那么要如何才能学好css,并很好的应用到实际开发中,这篇文章就推荐一些关于css相关的书籍给大家。CSS世界 以“流”为线索,从结构、内容到美化装饰等方面,全面且深入地讲解前端开发人员必须了解和掌握的大量的CSS知识点。同时,作者结合多年的从业经验,通过大量的

    2020/04/05
  • ctolib码库攻略教程_每日更新收录实用的开源项目和资源

    ctolib码库攻略教程 官方网址:https://www.ctolib.com/ 简介描述:每日更新收录实用的开源项目和资源 CTOLib码库分类收集GitHub上的开源项目,并…

    2020/03/07
  • psd2css小白指南_Photoshop投影转换为CSS3工具

    psd2css小白指南 官方网址:https://psd2css.mezw.com GitHub:# 简介描述:Photoshop投影转换为CSS3工具 将Photoshop中的投…

    2020/03/06
  • vueg入门基础_为Vue应用添加页面间的转场特效

    vueg入门基础 官方网址:3 GitHub:https://github.com/jaweii/Vueg—-page-transition-plugin 简介描述:为…

    2020/03/06
  • 前端UI攻城狮 你们该抛弃jQuery了使用指南_jquery菜鸟教程

    你不再需要jQuery!Web工程师太依赖jQuery了,某种意义上说jQuery已经成了JavaScript的同义词。但是我们真的需要他么?或许我们应该反思一下什么时候才真的需要jQuery。对我个人而言开始使用jQuery的理由是他把我的工作变得简单多了,开发Web应用已经几乎离不开它。曾经在不同浏览器里Web API的实现有很大区别,而jQuery帮我

    2020/03/24