浅谈 javascript 性能优化
Post on 26-Aug-2014
7.683 Views
Preview:
DESCRIPTION
TRANSCRIPT
浅谈 JavaScript 性能优化
龙刚 ( @RainoXu ) TaobaoUEDwww.rainoxu.com
优化 JavaScript 性能,使它运行足够快一个关键因素:运行的时间
响应时间与用户的体验• 0.1s– 用户觉得很流畅
• 1.0s– 用户的操作可能偶尔受到影响,并且用户已经能感觉到有些不流畅
• 10s– 对用户的影响比较严重,需要相应的进度提示。用户也会有一些沮丧
What To Do && How To Do?
• 管理作用域• 操作数据• 流控制• Reflow• DOM 操作• 长时间运行的脚本处理
管理作用域function add(num1, num2){
return num1 + num2; } var result = add(5, 10);
使用局部变量局部变量存在于活动对象中,解析器只需查找作用域中的单个对象var a = 1;function test(){
// 对变量 a进行一系列操作}
function test2(){var a = 1; // 对变量a进行一系列操作
}
另一个例子(function(win, S, undefined) {
...
... var doc = win['document'], loc = location, EMPTY = '',
...
...})(window, 'KISSY');
数据操作
使用局部变量,它是最快的缓存频繁使用的对象、数组及相关的属性值
obj.name 比 obj.xxx.name 访问更快,访问属性的速度,与其在对象中的深度有关“ . ” 操作的次数直接影响着访问对象属性的耗时var objName = obj.name;
KISSY.add('switchable', function(S, undefined) { var DOM = S.DOM, Event = S.Event,
...
...});
function process(data){if (data. count> 0){
for(var i = 0; i < data.count; i++){processData(data.item[i]);
}}
}
function process(data){var count = data.count;if (count > 0){for(var i = 0; i < count ; i++){processData(data.item[i]);}}
}
NodeList
不直接操作 NodeList ,将其转换成静态数组后再使用方法:
Array.prototype.slice.call() => 标准浏览器逐个拷贝到一个新数组中 => For IE
大部分 JS 库都有提供将 Array-Like 的对象转变成 Array 的方法(如 KISSY 提供的makeArray() 方法);部分 JS 库在返回元素集合时,已预处理成 Array (例子: YUI 的DOM 相关操作方法)
遍历 NodeList 时,不做对当前 NodeList 相关结构有影响的 DOM 操作,并且如之前所提到的,要缓存一些频繁使用到的属性值,以避免杯具发生。
var divs = document.getElementsByTagName('DIV');
// 假定页面中有 div ,所以 divs.length 是大于 0的for (var idx = 0; idx < divs.length; idx++){
document.body.appendChild(// 杯具悄然而置document.createElement('DIV')
);console.info(divs.length);
}
杯具的原因?通过 getElementsByTagName() 获取得到的是一个 Live NodeList 的引用,任何对其相关的 DOM 操作都会立即反应在这个NodeList 上面通过不断地往 document.body 下插入 div 节点, for 循环的终止条件( div.length 也随之改变)失效,陷入死循环。
Live NodeList vs Static NodeList
理论上,静态的东西应该是最快的,但是实际情况是, Live NodeList 更快。
Live NodeList vs Static NodeList
原因:目前市场上的浏览器,对 Live NodeList做了缓存
Live NodeList vs Static NodeList
结论:优先使用 Live NodeList ,通过选择器获取以后,再进一步转换成数组来使用。这也是目前许多 JS 库在使用的方案。
DOM 操作• 指明操作 DOM 的 context
YUI:Array getElementsByClassName ( className , tag , root , apply , o , overrides )
KISSY:Array<HTMLElement> query ( selector, context )
即便是用原生的 JS ,也应该指明 :context.getElementsByTagName()
DOM 操作• 增删、修改节点– 使用 DocumentFragment– 使用 cloneNode() 复制一份目标节点来处理– 如果是直接修改 DOM ,请先将其 display:none;
一个方法尽可能只做一件事拆分功能,让一个方法只做一件事,通过不断地调用方法来实现复杂功能,但是,这些简单方法要避免相互交叉调用。
KISSY Poster 中的一些方法拆分
KISSY Poster 中的一些方法拆分
Be Lazy使脚本尽可能少地运行,或者不运行。
短路表达式应用:如 a && b || c
基于事件去写相应的处理方法惰性函数
• 合理地使用事件代理DOM 与事件处理
为元素绑定事件Event.on ( target, type, fn, scope )
事件代理的原理冒泡 捕获
事件代理应用的场景?
Event.on(container, ‘click’, function (ev){var target = ev.target();
switch(target.className){//或者可以是 nodeName.........}
});
流控制
if(...){}elseif(...){}elseif(...){}elseif(...){}elseif(...){}elseif(...){}else{}
在 if语句中,将经常会发生的条件,放在靠上的位置if 的条件为连续的区间时,可以使用二分法的方式来拆分较多离散值的判断,可以使用 switch 来替代使用数组查询的方式
要注意隐式的类型转换var foo = 0;if(foo == false){
...}
小心递归!
function recurse(){recurse();
}recurse(); //又是一个杯具
• 浏览器对调用栈的最大限度的定义各不一样• 递归的相互调用、自身调用可能触发浏览器的调用栈的最大极限
Reflow
主要引起 Reflow 的因素• 操作 DOM树• 与布局有关的样式改变• 改变 className• 窗口大小调整• 字休大小
优化运行时间较长的脚本
• 原因:– 大量 DOM 操作– 过多的循环与递归
• 解决问题的最佳实践:– 使用定时器
最后,优化原则?
考虑大多数情况,极端情况,有能力则兼顾之,适当取舍2/8 原则
性能与可维护性权衡之一原则
YAHOO 的前端小组、 John Resig 、 Nicholas C.Zakas等都已经总结了很多有用的性能优化方面的经验,以他们的研究成果做为优化时的参考。
站在巨人的肩膀上,看得更远
• 不以善小而不为• 思先于行,不必过早优化好的编程习惯
最后,感谢玉伯、云谦、圆心、龙俊、释然对我此次的分享提供了许多帮助和建议。
Question?
top related