作者:魚頭的Web海洋 公號 / 陳大魚頭
此題目的答案可以分為三大類:
- 類型轉(zhuǎn)換時的劫持
首先我們要知道,在 JS 中類型轉(zhuǎn)換只有三種情況,分別是:
轉(zhuǎn)換為布爾值
轉(zhuǎn)換為數(shù)字
轉(zhuǎn)換為字符串
轉(zhuǎn)換為原始類型
對象在轉(zhuǎn)換類型的時候,會執(zhí)行原生方法ToPrimitive。
其算法如下:
如果已經(jīng)是 原始類型,則返回當前值;
如果需要轉(zhuǎn) 字符串 則先調(diào)用 toSting方法,如果此時是 原始類型 則直接返回,否則再調(diào)用 valueOf方法并返回結(jié)果;
如果不是 字符串,則先調(diào)用 valueOf方法,如果此時是 原始類型 則直接返回,否則再調(diào)用 toString方法并返回結(jié)果;
如果都沒有 原始類型 返回,則拋出 TypeError類型錯誤。
當然,我們可以通過重寫 Symbol.toPrimitive來制定轉(zhuǎn)換規(guī)則,此方法在轉(zhuǎn)原始類型時調(diào)用優(yōu)先級最高。
所以以此定義我們可以有以下四種答案:
?var a = {
arr: [3, 2, 1],
valueOf () {
console.group('valueOf')
console.log(this.arr)
console.groupEnd('valueOf')
return this.arr.pop()
}
}
if (a == 1 && a == 2 && a == 3) {
console.log('biu')
}
var b = {
arr: [3, 2, 1],
toString () {
console.group('toString')
console.log(this.arr)
console.groupEnd('toString')
return this.arr.pop()
}
}
if (b == 1 && b == 2 && b == 3) {
console.log('biu')
}
var c = {
arr: [3, 2, 1],
[Symbol.toPrimitive] () {
console.group('Symbol.toPrimitive')
console.log(this.arr)
console.groupEnd('Symbol.toPrimitive')
return this.arr.pop()
}
}
if (c == 1 && c == 2 && c == 3) {
console.log('biu')
}
var d = [1, 2, 3]
d.join = d.shift
if (d == 1 && d == 2 && d == 3) {
console.log('biu')
}
注:事實上,這四種可以算是同一種。關(guān)于最后一種,我們可以來看看ECMA中的 Array.prototype.toString() 定義:
定義 array 為 ToObject(thisvalue)(原生方法,將當前數(shù)組轉(zhuǎn)換成對象);
定義 func 為 Get(array,'join')(原生方法,在這一步調(diào)用 join 方法);
如果 IsCallble(func) (原生方法,判斷是否有內(nèi)部可調(diào)用的函數(shù))為 false,則 設置 func 原生函數(shù) %ObjProto_toString%(原生函數(shù), toString 的具體實現(xiàn));
返回 Call(func,array)。
- 對 getter 的劫持
所謂的 getter 就是對象屬性在進行查詢時會被調(diào)用的方法 get,利用此函數(shù)也可以實現(xiàn)題目功能。
代碼如下:
window.val = 0
Object.defineProperty(window, 'd', {
get () {
return ++this.val
}
})
if (d == 1 && d == 2 && d == 3) {
console.log('biu')
}
const e = new Proxy({}, {
val: 1,
get () {
return () => this.val++;
}
});
if (e == 1 && e == 2 && e == 3) {
console.log('biu')
}
- 正則表達式
JS 中的 RegExp.prototype.exec() 作用是在一個指定字符串中執(zhí)行一個搜索匹配,返回一個結(jié)果數(shù)組或 null。
當正則表達式使用 " g" 標志時,可以多次執(zhí)行 exec 方法來查找同一個字符串中的成功匹配。當你這樣做時,查找將從正則表達式的 lastIndex 屬性指定的位置開始。( test() 也會更新 lastIndex 屬性)。
lastIndex 是正則表達式的一個可讀可寫的整型屬性,用來指定下一次匹配的起始索引。只有正則表達式使用了表示全局檢索的 " g" 標志時,該屬性才會起作用。
注:只有正則表達式使用了表示全局檢索的 " g" 標志時,該屬性才會起作用。
綜上所述,我們可以有方案如下:
var f = {
reg: /\d/g,
valueOf () {
return this.reg.exec(123)[0]
}
}
if (f == 1 && f == 2 && f == 3) {
console.log('biu')
}
注:上述方法其實也利用了類型轉(zhuǎn)換的特點。然后暫時就寫下以上三種答案,不知道聰明的你是否還有別的解法呢?