欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解如何讓JavaScript代碼不可斷點

 更新時間:2022年08月05日 09:31:56   作者:EtherDream  
這篇文章主要為大家詳介紹了如何讓JavaScript代碼不可斷點,文中的示例代碼講解詳細,對我們學(xué)習(xí)JavaScript有一定幫助,感興趣的可以了解一下

繞過斷點

調(diào)試 JS 代碼時,單步執(zhí)行(F11)可跟蹤所有操作。例如這段代碼,每次調(diào)用 alert 時都會被斷住:

debugger
alert(11)
alert(22)
alert(33)
alert(44)

有沒有什么辦法能讓單步執(zhí)行失效,一次執(zhí)行多個操作?

事實上有一些巧妙的辦法。例如通過數(shù)組回調(diào)執(zhí)行這些 alert 函數(shù):

debugger
[11, 22, 33, 44].forEach(alert)

這樣只有 forEach 之前和之后會被斷住,中間所有 alert 調(diào)用都不會被斷住。

由此可見,通過 內(nèi)置回調(diào) 執(zhí)行 原生函數(shù),調(diào)試器是無法斷住的!

利用這個特性,我們可將一些重要的操作隱藏起來,從而能在調(diào)試者眼皮下悄悄執(zhí)行。

應(yīng)用案例

主流瀏覽器的調(diào)試器允許攔截特定事件,例如觸發(fā) mousemove 時斷點;

addEventListener('mousemove', e => {
  console.log(e)
})

因此調(diào)試者很容易找到事件回調(diào)函數(shù),從而分析相應(yīng)的處理邏輯。

如何防止事件回調(diào)被斷點?這就需要前面講解的黑科技了。我們對上述代碼稍微修改,將自己的回調(diào)函數(shù)改成原生函數(shù):

addEventListener('mousemove', console.log)

這時,每次觸發(fā) mousemove 事件都不會被斷??!

然而現(xiàn)實中的回調(diào)邏輯遠比 console.log 復(fù)雜,又該如何應(yīng)用?

事實上我們可以做一些調(diào)整,將事件的回調(diào)邏輯變得足夠簡單,簡單到只需一個操作 —— 保存結(jié)果:

const Q = []
addEventListener('mousemove', Q.push.bind(Q))

由于調(diào)用函數(shù) bind 方法后返回的新函數(shù),其實是原生的:

function A() {}
A.bind(window) + ''   // "function () { [native code] }"

而 Q.push 本身也是原生函數(shù),因此它們兩都是原生函數(shù)。

同時 addEventListener 執(zhí)行回調(diào)也屬于內(nèi)置行為,因此整個操作都是原生函數(shù)在執(zhí)行,沒有任何自己的代碼可供調(diào)試器斷點!

現(xiàn)在觸發(fā) mousemove 事件不僅不會被斷住,而且還能將結(jié)果追加到數(shù)組 Q 中。

至于讀取則有很多辦法,例如渲染事件、空閑事件、定期輪詢等。

setInterval(() => {
  for (const v of Q) {
    console.log(v)
  }
  Q.length = 0
}, 20)

如果 JS 只是采集信息而沒有交互,可用更低的讀取頻率。

屬性訪問

前面的案例都是函數(shù)調(diào)用,例如 alert 函數(shù)、數(shù)組 push 函數(shù)。但屬性讀寫又該如何實現(xiàn)?例如:

window.onclick = function() {
  document.title = 'hello'
}

其實也不難。屬性讀寫本質(zhì)上是 getter 和 setter 函數(shù)的調(diào)用。例如:

const setter = Object.getOwnPropertyDescriptor(Document.prototype, 'title').set
setter.call(document, 'hello')

當然這樣會立即執(zhí)行,而不是在 onclick 事件時執(zhí)行。

因此我們可以給 setter 柯里化,創(chuàng)建一個已綁定參數(shù)的新函數(shù),作為事件回調(diào):

const setter = Object.getOwnPropertyDescriptor(Document.prototype, 'title').set
window.onclick = setter.bind(document, 'hello')

這樣只有在點擊時才會執(zhí)行。并且調(diào)試器的 click 事件斷點不會觸發(fā)。

對象屬性

除了原型上的屬性,普通對象的屬性又該如何訪問?例如:

const obj = {}
window.onclick = function() {
  obj.name = 'jack'
}

事實上 JS 基本操作都可通過 Reflect API 實現(xiàn)。例如:

const obj = {}
Reflect.set(obj, 'name', 'jack')

不過需注意的是,Reflect.set 的參數(shù)必須是 3 個,多一個也不行。例如:

const obj = {}
Reflect.set(obj, 'age', 20, {})
obj.age   // undefined

這樣將其柯里化成事件回調(diào)函數(shù)是有問題的,因為事件回調(diào)還會加上一個 event 參數(shù)。

不過 Reflect.apply 方法倒沒有這個限制,往后再加幾個參數(shù)也不影響執(zhí)行:

Reflect.apply(alert, null, ['hello'],   /* 無用的參數(shù) */ 100, 200, 300)

因此我們可通過 Reflect.apply 執(zhí)行 Reflect.set,從而過濾多余的參數(shù):

const obj = {}
Reflect.apply(Reflect.set, null, [obj, 'age', 20])
obj.age   // 20

然后將其柯里化成事件回調(diào)函數(shù):

const obj = {}
window.onclick = Reflect.apply.bind(null, Reflect.set, null, [obj, 'age', 20])

這樣即可通過原生函數(shù)執(zhí)行 obj.age = 20,并且 click 事件斷點依然不會觸發(fā)。

多個操作

前面講解的都是單個操作,是否可以一次執(zhí)行多個操作?例如:

console.log('hello')
console.log('world')
alert(123)

最容易想到的辦法,就是將每個操作放入數(shù)組,然后通過 forEach 回調(diào) Reflect.apply 執(zhí)行每個操作:

[
  Reflect.apply.bind(null, console.log, null, ['hello']),
  Reflect.apply.bind(null, console.log, null, ['world']),
  Reflect.apply.bind(null, alert, null, [123]),
].forEach(Reflect.apply)

幸運的是 forEach 的回調(diào)函數(shù)和 Reflect.apply 函數(shù)都是 3 個參數(shù),并且第 3 個都是數(shù)組類型:

forEach_callback(element, index, array)

Reflect.apply(target, thisArgument, argumentsList)

這樣通過 forEach 回調(diào) Reflect.apply 是完全沒問題的。于是可以一次執(zhí)行多個操作,并且都無法斷住!

除了上述提到的,其實還有更多玩法,大家可發(fā)揮想象~

到此這篇關(guān)于詳解如何讓JavaScript代碼不可斷點的文章就介紹到這了,更多相關(guān)JavaScript代碼不斷點內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論