转
2017 年 11 月 02 日 15:11:06
1:JS 中大量的 DOM 操作也会导致事件响应缓慢甚至真正卡死浏览器, 如在 IE6 下一次插入大量的 html. 而如果真的弹出了 "脚本运行时间过长" 的提示框则说明你的 JS 脚本肯定有死循环或者进行过深的递归操作了.
2: 这时候需要对脚本进行优化, 其步骤如下:
第一步, 优化循环, 循环体中包含太多的操作和循环的次数过多都会导致循环执行时间过长, 并直接导致锁死浏览器. 如果循环之后没有其他操作, 每次循环只处理一个数值, 而且不依赖于上一次循环的结果则可以对循环进行拆解, 看下面的 chunk 的函数:
- function chunk(array, process, context){
- setTimeout(function(){
- var item = array.shift();
- process.call(context, item);
- if(array.length>0){
- setTimeout(arguments.callee,100);
- }),100);
- }
chunk()函数的用途就是将一个数组分成小块处理, 它接受三个参数: 要处理的数组, 处理函数以及可选的上下文环境. 每次函数都会将数组中第一个对象取出交给 process 函数处理, 如果数组中还有对象没有被处理则启动下一个 timer, 直到数组处理完. 这样可保证脚本不会长时间占用处理机, 使浏览器出一个高响应的流畅状态.
其实看来, 借助 JS 强大的闭包机制任何循环都是可拆分的, 下面的版本增加了 callback 机制, 使可再循环处理完毕之后进行其他的操作.
- function chunk(array,process,cbfun){
- var i=0,len = array.length; // 这里要注意在执行过程中数组最好是不变的
- setTimeout(function(){
- process( array[i], i++); // 循环体要做的操作
- if( i < len ){
- setTimeout(arguments.callee,100)
- }else{
- cbfun() // 循环结束之后要做的操作
- }
- }
- }
第二步, 优化函数, 如果函数体内有太多不相干但又要一起执行的操作则可以进行拆分, 考虑下面的函数:
- function dosomething(){
- dosomething1();
- dosomething2();
- }
dosomething1 和 dosomething2 互不相干, 执行没有先后次序, 可用前面提到的 chunk 函数进行拆分:
- function dosomething(){
- chunk([dosomething1,dosomething2],function(item){item();})
- }
或者直接交给浏览器去调度
- function dosome(){
- setTimeout(dosomething1,0);
- setTimeout(dosomething2,0);
- }
第三步, 优化递归操作, 函数递归虽然简单直接但是过深的递归操作不但影响性能而且稍不注意就会导致浏览器弹出脚本失控对话框, 必须小心处理.
看以下斐波那契数列的递归算法:
- function fibonacci(n){
- return n <2? n: fibonacci(n -1)+ fibonacci(n -2);
- };
fibonacci(40)这条语句将重复调用自身 331160280 次, 在浏览器中执行必然导致脚本失控, 而采用下面的算法则只需要调用 40 次
- fibonacci =function(n){
- var memo ={0:0,1:0}; // 计算结果缓存
- var shell =function(n){
- var result = memo[n];
- if(typeof result !='number') // 如果值没有被计算则进行计算
- memo[n]= shell(n-1)+ shell(n -2)
- return memo[n];
- }
- return shell(n);
- }
这项技术被称为 memoization, 他的原理很简单就是同样的结果没必要计算两次. 另一种消除递归的办法就是利用迭代, 递归和迭代经常会被作为互相弥补的方法.
第四步, 减少 DOM 操作, DOM 操作的代价是相当昂贵的, 大多数 DOM 操作都会触发浏览器的回流 (reflow) 操作. 例如添加删除节点, 修改元素样式, 获取需要经过计算的元素样式等. 我们要做的就是尽量少的触发回流操作.
el.style.width ='300px' el.style.height ='300px' el.style.backgroundColor ='red'
上面的操作会触发浏览器的三次回流操作, 再看下面的方式:
el.className ='newStyle'
通过设置改元素的 className 一次设置多个样式属性, 将样式写再 CSS 文件中, 只触发一次回流, 达到了同样是效果而且效率更高. 因为浏览器最擅长的就是根据 class 设置样式.
还有很多可以减少 DOM 操作的方法, 在此就不多说了, 但是一个基本的原则就是让浏览器去做它自己擅长的事情, 例如通过 class 来改变元素的属性.
相信经过上面的优化的过程必定可以大大提高用户体验, 不会出现浏览器被锁死和弹出脚本失控的对话框, 使你的浏览器从繁重的任务中解放出来. 需要指出的是上面这些优化并不是必须的, 只有当一段脚本的执行时间真的影响到了用户体验才需要进行. 虽然让用户觉得脚本的执行变快了, 但其实完成同一个操作的时间可能被延长了, 这些技术只是让浏览器处于一个快速响应的状态, 使浏览更流畅.
来源: https://blog.csdn.net/qq_37332557/article/details/78425181