当我第一次看到这一题目的时候,我是比拟震惊的,剖析了下很不合咱们编程的常理,并认为不大可能,变量 a
要在同一状况下要同时等于 1,2 和 3 这三个值,这是天方夜谭吧,不亚于哥德巴赫 1+1=1
的猜测吧,不过所有皆有可能,出于好奇心,想了许久之后我还是决定尝试解决的方法。
我的思路来源于更早前遇到的另外一题类似的面试题:
// 设置一个函数输入一下的值
f(1) = 1;
f(1)(2) = 3;
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
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
的有限可能是具备启发性的,也倡议面试官不要应用这类面试题去难为面试者~
如果文章和笔记能带您一丝帮忙或者启发,请不要悭吝你的赞和珍藏,你的必定是我后退的最大能源😁
附笔记链接,浏览往期更多优质文章可移步查看,喜爱的能够给我点赞激励哦:
- https://github.com/Wscats/CV/…