大廠面試題分享:如何讓(a===1& &a===2& &a===3)的值為true?

當我第一次看到這一題目的時候,我是比較震驚的,分析了下很不合我們編程的常理,並認為不大可能,變量a要在同一情況下要同時等於1,2和3這三個值,這是天方夜譚吧,不亞於哥德巴赫1+1=1的猜想吧,不過一切皆有可能,出於好奇心,想了許久之後我還是決定嘗試解決的辦法。

我的思路來源於更早前遇到的另外一題相似的面試題:

<code>// 設置一個函數輸出一下的值
f(1) = 1;
f(1)(2) = 2;
f(1)(2)(3) = 6;/<code>

當時的解決辦法是使用toString或者valueOf實現的,那我們先回顧下toString和valueOf方法,方便我們更深入去了解這類型的問題:

比如我們有一個對象,在不重寫toString()方法和valueOf()方法的情況下,在 Node 或者瀏覽器輸出的結果是這樣的

<code>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/<code>

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 方法進行一次複寫:

<code>class Person {
constructor(name) {
this.name = name;
}
// 複寫 valueOf 方法
valueOf() {
return this.name;
}
}/<code>

best Person best.toString() [object Object] best.valueOf() Person best + 'GiGi' KobeGiGi

這次跟上面只有一處產生了不一樣的結果,那就是最後的best + 'GiGi'前後兩次結果在複寫了valueOf()方法之後發生了改變,從中我們可以看出來,對象的本質其實沒有發生根本的改變,但是當它被用作直接運算的時候,它的值是從複寫的valueOf()中獲取的,並繼續參與後續的運算。

當然不要忘了我們還有個toString()方法,所以我們也複寫它,看看結果會不會也受影響:

<code>class Person {
constructor(name) {
this.name = name;
}
valueOf() {
return this.name;
}
toString() {
return `Bye ${this.name}`;
}
}/<code>

best Person best.toString() Bye Kobe best.valueOf() Kobe best + 'GiGi' KobeGiGi

我們發現 best + 'GiGi'還是沒有發生任何改變,還是使用我們上一次複寫valueOf()的結果

其實我們重寫了valueOf方法,不是一定調用valueOf()的返回值進行計算的。而是valueOf返回的值是基本數據類型時才會按照此值進行計算,如果不是基本數據類型,則將使用toString()方法返回的值進行計算。

<code>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/<code>

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()方法。

所以鋪墊了這麼久,我們就要揭開答案,我們正是使用上面這些原理去解答這一題:

<code>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!");
}/<code>

這裡就比較簡單,直接改寫toString()方法,由於沒有valueOf(),當他做運算判斷a == 1的時候會執行toString()的結果。

<code>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!");
}/<code>

當然,你也可以不使用toString,換成valueOf也行,效果也是一樣的:

<code>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!");
}/<code>

所以,當一個對象在做運算的時候(比如加減乘除,判斷相等)時候,往往會有valueOf()或者toString的調用問題,這個對象的變量背後通常隱藏著一個函數。

當然下面這題原理其實也是一樣的,附上解法:

<code>// 設置一個函數輸出一下的值
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/<code>

當然還沒有結束,這裡還會有一些特別的解法,其實在使用對象的時候,如果對象是一個數組的話,那麼上面的邏輯還是會成立,但此時的toString()會變成隱式調用join()方法,換句話說,對象中如果是數組,當你不重寫其它的toString()方法,其默認實現就是調用數組的join()方法返回值作為toString()的返回值,所以這題又多了一個新的解法,就是在不復寫toString()的前提下,複寫join()方法,把它變成shift()方法,它能讓數組的第一個元素從其中刪除,並返回第一個元素的值。

<code>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!");
}/<code>

我們的探尋之路還沒結束,細心的同學會發現我們題目是如何讓(a===1&&a===2&&a===3)的值為 true,但是上面都是討論寬鬆相等==的情況,在嚴格相等===的情況下,上面的結果會不同嗎?

答案是不一樣的,你們可以試試把剛才上面的寬鬆條件改成嚴格調試再試一次就知道結果了。

<code>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!此時再也沒出現過了
}/<code>

那麼此時的情況又要怎麼去解決呢?我們可以考慮一下使用Object.defineProperty來解決,這個因為Vue而被眾人熟知的方法,也是現在面試中一個老生常談的知識點了,我們可以使用它來劫持a變量,當我們獲取它的值得時候讓它自增,那麼問題就可以迎刃而解了:

<code>var value = 1;
Object.defineProperty(window, "a", {
get() {
return this.value++;
}
});

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

}/<code>

上面我們就是劫持全局window上面的a,當a每一次做判斷的時候都會觸發get屬性獲取值,並且每一次獲取值都會觸發一次函數實行一次自增,判斷三次就自增三次,所以最後會讓公式成立。

當然這裡還有其他方法,這裡再舉例一個,比如使用隱藏字符去做障眼法瞞過面試官的:

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

上面這種解法的迷惑性很強,如果不細心會以為是三個一樣的a,其實本質上是定義三個不一樣的a值,a的前後都有隱藏的字符,所以調試的時候,請複製粘貼上面的代碼調試,自己在Chrome手打的話可以用特殊手段讓 a 後面放一個或者兩個紅點實現,並在回車的時候,調試工具會把這些痕跡給隱藏,從而瞞天過海,秀到一時半刻還沒反應過來的面試官。

面試題庫分享

篇幅有限,需要完整面試文檔轉發收藏+關注私信“Java面試”即可獲取

大廠面試題分享:如何讓(a===1& &a===2& &a===3)的值為true?

大廠面試題分享:如何讓(a===1& &a===2& &a===3)的值為true?

獲取方式:轉發收藏+關注私信“面試”即可免費領取

我們與其羨慕他人的成功,不如從此刻開始,積累足夠多的知識和麵試經驗,為將來進入更好的公司做充分的準備!機會永遠屬於有準備的人~希望大家學完之後,都能拿到自己心儀的 offer!!


作者:EnoYao
鏈接:https://juejin.im/post/5e66dc416fb9a07cab3aaa0a
來源:掘金


分享到:


相關文章: