如何让 JS 代码不可断点
绕过断点
调试 JS 代码时,单步执行(F11)可跟踪所有操作。例如这段代码,每次调用 alert 时都会被断住:
debugger
alert(11)
alert(22)
alert(33)
alert(44)
有没有什么办法能让单步执行失效,一次执行多个操作?
事实上有一些巧妙的办法。例如通过数组回调执行这些 alert 函数:
debugger
[11, 22, 33, 44].forEach(alert)
这样只有 forEach 之前和之后会被断住,中间所有 alert 调用都不会被断住。
由此可见,通过 内置回调 执行 原生函数,调试器是无法断住的!
利用这个特性,我们可将一些重要的操作隐藏起来,从而能在调试者眼皮下悄悄执行。
应用案例
主流浏览器的调试器允许拦截特定事件,例如触发 mousemove 时断点;
addEventListener('mousemove', e => {
console.log(e)
})
因此调试者很容易找到事件回调函数,从而分析相应的处理逻辑。
如何防止事件回调被断点?这就需要前面讲解的黑科技了。我们对上述代码稍微修改,将自己的回调函数改成原生函数:
addEventListener('mousemove', console.log)
这时,每次触发 mousemove 事件都不会被断住!
然而现实中的回调逻辑远比 console.log 复杂,又该如何应用?
事实上我们可以做一些调整,将事件的回调逻辑变得足够简单,简单到只需一个操作 —— 保存结果:
const Q = []
addEventListener('mousemove', Q.push.bind(Q))
现在触发 mousemove 事件不仅不会被断住,而且还能将结果追加到数组 Q 中。
至于读取则有很多办法,例如渲染事件、空闲事件、定期轮询等。
setInterval(() => {
for (const v of Q) {
console.log(v)
}
Q.length = 0
}, 20)
如果 JS 只是采集信息而没有交互,可用更低的读取频率。
属性访问
前面的案例都是函数调用,例如 alert 函数、数组 push 函数。但属性读写又该如何实现?例如:
window.onclick = function() {
document.title = 'hello'
}
其实也不难。属性读写本质上是 getter 和 setter 函数的调用。例如:
const setter = Object.getOwnPropertyDescriptor(Document.prototype, 'title').set
setter.call(document, 'hello')
当然这样会立即执行,而不是在 onclick 事件时执行。
因此我们可以给 setter 柯里化,创建一个已绑定参数的新函数,作为事件回调:
const setter = Object.getOwnPropertyDescriptor(Document.prototype, 'title').set
onclick = setter.bind(document, 'hello')
这样只有在点击时才会执行。并且调试器的 click 事件断点不会触发。
对象属性
除了原型上的属性,普通对象的属性又该如何访问?例如:
const obj = {}
window.onclick = function() {
obj.name = 'jack'
}
事实上 JS 基本操作都可通过 Reflect API 实现。例如:
const obj = {}
Reflect.set(obj, 'name', 'jack')
不过需注意的是,Reflect.set
的参数必须是 3 个,多一个也不行。例如:
const obj = {}
Reflect.set(obj, 'age', 20, {})
obj.age // undefined
这样将其柯里化成事件回调函数是有问题的,因为事件回调还会加上一个 event 参数。
不过 Reflect.apply
方法倒没有这个限制,往后再加几个参数也不影响执行:
Reflect.apply(alert, null, ['hello'], 100, 200, 300)
因此我们可通过 Reflect.apply
执行 Reflect.set
,从而过滤多余的参数:
const obj = {}
Reflect.apply(Reflect.set, null, [obj, 'age', 20])
obj.age // 20
然后将其柯里化成事件回调函数:
const obj = {}
onclick = Reflect.apply.bind(null, Reflect.set, null, [obj, 'age', 20])
这样即可通过原生函数执行 obj.age = 20,并且 click 事件断点依然不会触发。
多个操作
前面讲解的都是单个操作,是否可以一次执行多个操作?例如:
console.log('hello')
console.log('world')
alert(123)
最容易想到的办法,就是将每个操作放入数组,然后通过 forEach
回调 Reflect.apply
执行每个操作:
[
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
的回调函数和 Reflect.apply
函数都是 3 个参数,并且第 3 个都是数组类型:
forEach_callback(element, index, array)
Reflect.apply(target, thisArgument, argumentsList)
这样通过 forEach
回调 Reflect.apply
是完全没问题的。于是可以一次执行多个操作,并且都无法断住!
除了上述提到的,其实还有更多玩法,大家可发挥想象~
(2021/11/01)
如何让 JS 代码不可断点的更多相关文章
- Firebug调试js代码
Firebug功能异常强大,不仅可以调试DOM,CSS,还可以调试JS代码,下面介绍一下调试JS. 1.认识console对象 console对象是Firebug内置的对象,该对象可以在代码中写入,可 ...
- js数组特定位置元素置空,非null和undefined,实现echarts现状图效果;谷歌格式化压缩js代码
一.想要实现eCharts线状图表的断点效果,如图(后来又查到数据格式为data:['-', 2, 3,'-' , 5, 6, 7]:也可以断点显示) 这种效果,在设置数据的时候应该是这样: data ...
- Javascript系列: Google Chrome调试js代码(zz)
你 是怎么调试 JavaScript 程序的?最原始的方法是用 alert() 在页面上打印内容,稍微改进一点的方法是用 console.log() 在 JavaScript 控制台上输出内容.嗯~, ...
- webpages框架使用@razor语法向js代码传递Json字符串
进入web开发时间太短,一个人尝试着做了几个初级项目,遇到了太多的困难.尽管不是学开发专业的,仅为爱好所以硬着头皮坚持了下来. 将遇到的问题记录下来,备查. 使用vs2015中asp.net razo ...
- 初探内联方式的 onload="doSomething()"为何要加"()"?而js代码的 onload="doSomething" 和 addEventListener 为何不加"()"?
问题引入:在看<Jquery基础教程>第四版的时,P34页有这样一段话 引用函数与调用函数 这里在将函数指定为处理程序时,省略了后面的圆括号,只使用了函数名.如果带着圆括号,函数会被立即调 ...
- 如何用浏览器调试js代码
按F12打开调试工具
- Google Chrome调试js代码
你 是怎么调试 JavaScript 程序的?最原始的方法是用 alert() 在页面上打印内容,稍微改进一点的方法是用 console.log() 在 JavaScript 控制台上输出内容.嗯~, ...
- 如何查找元素对应事件的js代码,检测定位js事件
比如一张图片当鼠标放到上面时,图片改变.想找到这个事件对应的js代码,假设另存为html之后,文件夹中有.js文件. 如果你会调试,可以用打开浏览器的调试功能,以chrome为例,按F12打开调试窗口 ...
- js分析 快速定位 js 代码, 还原被混淆压缩的 js 代码
-1.目录 0.参考 1.页面表现 2. 慢镜头观察:低速网络请求 3. 从头到尾调试:Fiddler 拦截 index.html 并添加 debugger; 4. 快速定位 js 代码 5. 还原被 ...
随机推荐
- 利用ArcEngine开发地图发布服务,将mxd文档一键发布成wmts,并根据需要对地图进行空间查询,返回客户端geojson
一直想开发一个软件取代ArcGIS Server,该软件使用ArcEngine开发,以Windows Service形式发布,部署在服务端上,解决wmts地图服务发布和空间查询的问题,经过不断的研究. ...
- SQL表的创建
一,创建表 1.使用鼠标创建表 1,进入SQL进行连接 编辑 2,在左边会有一个对象资源管理器,右键数据库,在弹出的窗口中选择新建数据库 编辑 3,给这个包取个名字,在这个界面可以给这个表选 ...
- pip下载更改为清华镜像
step1: + 在user(用户)下新建一文件夹再在该文件夹下新建pip.ini文件 + 例如:user/pip/pip.ini + tips:如果未打开在查看里的隐藏扩展名记得打开 step2: ...
- 聚类--DBSCN
1.什么是DBSCN DBSCAN也是一个非常有用的聚类算法. 它的主要优点:它不需要用户先验地设置簇的个数,可以划分具有复杂形状的簇,还可以找出不属于任何簇的点. DBSCAN比凝聚聚类和k均值稍慢 ...
- Go微服务框架go-kratos实战02:proto 代码生成和编码实现步骤
在上一篇 kratos quickstart 文章中,我们直接用 kratos new 命令生成了一个项目. 这一篇来看看 kratos API 的定义和使用. 一.kratos 中 API 简介 1 ...
- Java的标识符与关键字
目录 Java关键字 总表:java关键字共53个(其中包含两个保留字const,goto) Java标识符 定义 组成 命名规则 视频课程 Java关键字 Java关键字是电脑语言里事先定义的,有特 ...
- 探究Presto SQL引擎(3)-代码生成
vivo 互联网服务器团队- Shuai Guangying 探究Presto SQL引擎 系列:第1篇<探究Presto SQL引擎(1)-巧用Antlr>介绍了Antlr的基本用法 ...
- ExtJS 同行表单域对齐有误处理办法
更新记录 2022年5月29日 第一次编辑.使用的ExtJS版本:ExtJS 7.4 问题 原本都是显示正常的表单域,比如这些文本框.选择框都是正常. 在用户进行操作,然后显示验证提示后,明显出现了问 ...
- 彰显个性│制作一个独一无二的动态 svg 头像
一.头像预览 看一下博主的动态图像,是不是很炫酷,想不想拥有一个? 这是一个 svg 图片,svg 图片不仅可以通过制图软件制作外,其实也可以通过代码进行开发 因为 svg 本质上是一个下 xml 文 ...
- bitmap技术解析:redis与roaringBitmap
bitmap的表象意义是,使用一个01标识位来表示是否的状态,可以达到节省空间和高效判定的效果.在我们的实际工作中,也有着许多的应用场景,相信了解bitmap定会给你带来一些额外的收获. 1. bit ...