查了好多资料,发现还是不全,干脆自己整理吧,至少保证在我的做法正确的,以免误导读者,也是给自己做个记载吧!
Javascript是一门非常灵活的语言,我们可以随心所欲的誊写各种风格的代码,不同风格的代码也必定也会导致执行效率的差异,开发过程中零零散散地接触到许多进步代码性能的方法,整理一下平常比拟常见并且轻易规避的问题
Javascript自身执行效率
Javascript中的作用域链、闭包、原型继承、eval等特性,在提供各种奇异功能的同时也带来了各种效率问题,用之不慎就会导致执行效率低下。
1、全局导入
我们在编码过程中多多少少会应用到一些全局变量(window,document,自定义全局变量等等),懂得javascript作用域链的人都知道,在局部作用域中拜访全局变量需要一层一层遍历全部作用域链直至顶级作用域,而局部变量的拜访效率则会更快更高,因此在局部作用域中高频率应用一些全局对象时可以将其导入到局部作用域中,例如:
1 //1、作为参数传入模块 2 (function(window,$){ 3 var xxx = window.xxx; 4 $("#xxx1").xxx(); 5 $("#xxx2").xxx(); 6 })(window,jQuery); 7 8 //2、暂存到局部变量 9 function(){ 10 var doc = document; 11 var global = window.global; 12 }
2、eval以及类eval问题
我们都知道eval可以将一段字符串当做js代码来执行处理,据说应用eval执行的代码比不应用eval的代码慢100倍以上(详细效率我没有测试,有兴致同学可以测试一下)
JavaScript 代码在执行前会停止类似“预编译”的操作:首先会创立一个当前执行环境下的活动对象,并将那些用 var 声名的变量设置为活动对象的属性,但是此时这些变量的赋值都是 undefined,并将那些以 function 定义的函数也添加为活动对象的属性,而且它们的值正是函数的定义。但是,如果你应用了“eval”,则“eval”中的代码(实际上为字符串)没法事后识别其上下文,没法被提前剖析和优化,即没法停止预编译的操作。所以,其性能也会大幅度下降
其实当初大家一般都很少会用eval了,这里我想说的是两个类eval的场景(new Function{}
,setTimeout
,setInterver
)
setTimtout("alert(1)",1000); setInterver("alert(1)",1000); (new Function("alert(1)"))();
3、闭包结束后释放掉不再被引用的变量
var f = (function(){ var a = {name:"var3"}; var b = ["var1","var2"]; var c = document.getElementByTagName("li"); //****其它变量 //***一些运算 var res = function(){ alert(a.name); } return res; })()
var f = (function(){ var a = {name:"var3"}; var b = ["var1","var2"]; var c = document.getElementByTagName("li"); //****其它变量 //***一些运算 //闭包返回前释放掉不再应用的变量 b = c = null; var res = function(){ alert(a.name); } return res; })()
Js操作dom的效率
在web开发过程中,前端执行效率的瓶颈往往都是在dom操作上面,dom操作是一件很耗性能的事件,如何才能在dom操作过程中尽量节约性能呢?
1、减少reflow
什么是reflow?
当 DOM 元素的属性产生变更 (如 color) 时, 浏览器会通知 render 从新描写响应的元素, 此过程称为 repaint。
如果该次变更涉及元素布局 (如 width), 浏览器则摈弃原有属性, 从新盘算并把结果传递给 render 以从新描写页面元素, 此过程称为 reflow。
减少reflow的方法
-
先将元素从document中删除,完成修改后再把元素放回本来的位置(当对某元素及其子元素停止大量reflow操作时,1,2两种方法效果才会比拟显著)
-
将元素的display设置为”none”,完成修改后再把display修改为本来的值
- 修改多个样式属性时定义class类取代多次修改style属性(for certain同学推荐)
-
大量添加元素到页面时应用documentFragment
例如
for(var i=0;i<100:i++){ var child = docuemnt.createElement("li"); child.innerHtml = "child"; document.getElementById("parent").appendChild(child); }
var frag = document.createDocumentFragment(); for(var i=0;i<100:i++){ var child = docuemnt.createElement("li"); child.innerHtml = "child"; frag.appendChild(child); } document.getElementById("parent").appendChild(frag);
2、暂存dom状态信息
当代码中需要多次拜访元素的状态信息,在状态不变的情况下我们可以将其暂存到变量中,这样可以防止多次拜访dom带来内存的开销,典型的例子就是:
var lis = document.getElementByTagName("li"); for(var i=1;i<lis.length;i++){ //*** } 上述方式会在每一次循环都去拜访dom元素,我们可以简略将代码优化如下 var lis = document.getElementByTagName("li"); for(var i=1,j=lis.length ;i<j;i++){ //*** }
3、缩小选择器的查找范围
查找dom元素时尽量防止大面积遍历页面元素,尽量应用精准选择器,或者指定上下文以缩小查找范围,以jquery为例
- 少用模糊匹配的选择器:例如
$("[name*='_fix']")
,多用诸如id以及逐渐缩小范围的复合选择器$("li.active")
等 - 指定上下文:例如
$("#parent .class")
,$(".class",$el)
等
4、应用事件委托
应用场景:一个有大量记载的列表,每条记载都需要绑定点击事件,在鼠标点击后实现某些功能,我们平日的做法是给每条记载都绑定监听事件,这种做法会导致页面会有大量的事件监听器,效率比拟低下。
基本原理:我们都知道dom规范中事件是会冒泡的,也就是说在不主动阻挠事件冒泡的情况下任何一个元素的事件都会按照dom树的结构逐级冒泡至顶端。而event对象中也提供了event.target(IE下是srcElement)指向事件源,因此我们即使在父级元素上监听该事件也可以找到触发该事件的最原始的元素,这就是委托的基本原理。废话不多说,上示例
$("ul li").bind("click",function(){ alert($(this).attr("data")); })
根据上面介绍的监听事件的原理我们来改写一下
$("ul").bind("click",function(e){ if(e.target.nodeName.toLowerCase() ==="li"){ alert($(e.target).attr("data")); } })
当然,我们不必每次都做事件源的判断任务,可以将其抽象一下交给工具类来完成。jquery中的delegate()方法就实现了该功能
语法是这样的$(selector).delegate(childSelector,event,data,function)
,例如:
$("div").delegate("button","click",function(){ $("p").slideToggle(); });
参数说明(引自w3school)
参数 | 描述 |
---|---|
childSelector | 必须。划定要附加事件处理程序的一个或多个子元素。 |
event | 必须。划定附加到元素的一个或多个事件。由空格分隔多个事件值。必须是有效的事件。 |
data | 可选。划定传递到函数的额定数据。 |
function | 必须。划定当事件产生时运行的函数。 |
Tips:事件委托还有一个利益就是,即使在事件绑定之后动态添加的元素上触发的事件同样可以监听到哦,这样就不用在每次动态参加元素到页面后都为其绑定事件了
临时先总结到这
文章结束给大家分享下程序员的一些笑话语录: 看新闻说中国输入法全球第一!领先了又如何?西方文字根本不需要输入法。一点可比性都没有。