此題目的答案可以分為三大類:
1. 類型轉換時的劫持
首先我們要知道,在 JS 中類型轉換只有三種情況,分別是:
轉換為布爾值
轉換為數字
轉換為字符串
轉換為原始類型
對象在轉換類型的時候,會執行原生方法ToPrimitive。
其算法如下:
1.如果已經是 原始類型,則返回當前值;
2.如果需要轉 字符串 則先調用 toSting方法,如果此時是 原始類型 則直接返回,否則再調用 valueOf方法并返回結果;
3.如果不是 字符串,則先調用 valueOf方法,如果此時是 原始類型 則直接返回,否則再調用 toString方法并返回結果;
4.如果都沒有 原始類型 返回,則拋出 TypeError類型錯誤。
當然,我們可以通過重寫 Symbol.toPrimitive來制定轉換規則,此方法在轉原始類型時調用優先級最高。
所以以此定義我們可以有以下四種答案:
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')
}
注:事實上,這四種可以算是同一種。關于最后一種,我們可以來看看ECMA中的 Array.prototype.toString() 定義:
1.定義 array 為 ToObject(thisvalue)(原生方法,將當前數組轉換成對象);
2.定義 func 為 Get(array,'join')(原生方法,在這一步調用 join 方法);
3.如果 IsCallble(func) (原生方法,判斷是否有內部可調用的函數)為 false,則 設置 func 原生函數 %ObjProto_toString%(原生函數, toString 的具體實現);
4.返回 Call(func,array)。
2. 對 getter 的劫持
所謂的 getter 就是對象屬性在進行查詢時會被調用的方法 get,利用此函數也可以實現題目功能。
代碼如下:
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')
}
3. 正則表達式
JS 中的 RegExp.prototype.exec() 作用是在一個指定字符串中執行一個搜索匹配,返回一個結果數組或 null。
當正則表達式使用 " g" 標志時,可以多次執行 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')
}