这篇文章来源于看了以下这篇文章和游戏后所想所写:
网页颜色分辨测试小游戏的js化辨别及优化
测测你的眼睛对色差的辨识度
游戏也是一个很简易的网页小游戏。
作者使用F12发现图形块颜色都是在前端生成这一小漏洞,写了一个小工具自动化闯关。
// 作者源码
//取到所有background
var stylelist = new Array();
$("#box span").each(function() {
for(var i = 0; i < $("#box span").length; i++) {
stylelist[i] = $(this).attr("style");
}
});
//分割数组
var s = stylelist.join(",") + ",",
copy;
for(var i = 0; i < stylelist.length; i++) {
//取出唯一style
if(s.replace(stylelist[i] + ",", "").indexOf(stylelist[i] + ",") > -1) {
copy = stylelist[i];
}
}
//不解释了吧
$("#box span").each(function() {
if($(this).attr("style") != copy)
$(this).click();
return;
});
分割数组那块当时没看懂,后来懂了。
s
保存着所有span
的style
值的join
连接后的字串,将字串用replace
替换为空值
,然后用indexOf在stylelist
中查找,如果有值(0 > -1 )
说明此值会重复,否则此值为only ,然后保存好重复的style值,对比替换即可。
原UP评论下有同学提到可以暴力循环点击每个元素,试了下发现可行,分数也比原来提高很多。(4000 > 7000),想来,一是省略了很多for循环,取元素的步骤,的确提高性能。
我在后续使用原生JS重写了一次,然后在60s中跑出了如下成绩:
document.getElementsByClassName("btn")[0].addEventListener("click", function () {
var fn = function () {
var length = document.querySelectorAll("#box span").length;
for (var i = 0; i < length; i++) {
document.querySelectorAll("#box span")[i].click();
}
};
setInterval(fn, 0);
})
最后想进一步优化,setInterval
使用的是JS引擎的event loop
模型,其回调会放入事件队列中等待主线程任务执行完毕后才会执行回调,那么可以使用for
循环代替setInterval
来省略这其中等待的时间吗?
document.getElementsByClassName("btn")[0].addEventListener("click", function () {
var fn = function () {
var arr = document.querySelectorAll("#box span");
var length = arr.length;
for (var i = 0; i < length; i++) {
arr[i].click();
}
};
for (var j = 0; j < 30000; j++) {
fn();
}
})
点击开始测试按钮后,页面没有了响应(也无法刷新),为什么?
我的想法是,这个游戏本身的计时器也是使用setInterval
或者setTimeout
来计时的,for
循环在执行这30000
次循环时候,主线程一直在工作,所以事件队列内的回调函数无法执行,当然没有响应,并且我们也知道在浏览器中JS执行的时候,其浏览器的渲染引擎也是被阻塞,不工作的,所以在页面上我们看不到任何的色块。
而且因为这种操作十分占用CPU,导致整个Chrome进程都巨卡无比,差点浏览器就崩溃了。
点击按钮后我开了个秒表,大慨在49s左右,主线程执行完毕,瞬间页面就跳了出来。
看到左上角的30000了吗?哈哈。
感谢原作者的文章,让我对一些游戏外挂有了全新的认识。