js性能提升建议

一.执行效率 
1. DOM 
1.1 使用DocumentFragment优化多次append 
说明:添加多个dom元素时,先将元素append到DocumentFragment中,最后统一将DocumentFragment添加到页面。 
该做法可以减少页面渲染dom元素的次数。经IE和Fx下测试,在append1000个元素时,效率能提高10%-30%,Fx下提升较为明显。
服用前: 
for (var i = 0; i < 1000; i++) { 
    var el = document.createElement('p'); 
    el.innerHTML = i; 
    document.body.appendChild(el); 

服用后: 
var frag = document.createDocumentFragment(); 
for (var i = 0; i < 1000; i++) { 
    var el = document.createElement('p'); 
    el.innerHTML = i; 
    frag.appendChild(el); 

document.body.appendChild(frag); 
1.2 通过模板元素clone,替代createElement 
说明:通过一个模板dom对象cloneNode,效率比直接创建element高。 
性能提高不明显,约为10%左右。在低于100个元素create和append操作时,没有优势。 
服用前: 
var frag = document.createDocumentFragment(); 
for (var i = 0; i < 1000; i++) { 
    var el = document.createElement('p'); 
    el.innerHTML = i; 
    frag.appendChild(el); 

document.body.appendChild(frag);    
服用后: 
var frag = document.createDocumentFragment(); 
var pEl = document.getElementsByTagName('p')[0]; 
for (var i = 0; i < 1000; i++) { 
    var el = pEl.cloneNode(false); 
    el.innerHTML = i; 
    frag.appendChild(el); 

document.body.appendChild(frag); 
1.3 使用一次innerHTML赋值代替构建dom元素 
说明:根据数据构建列表样式的时候,使用设置列表容器innerHTML的方式,比构建dom元素并append到页面中的方式,效率有数量级上的提高。 
服用前: 
var frag = document.createDocumentFragment(); 
for (var i = 0; i < 1000; i++) { 
    var el = document.createElement('p'); 
    el.innerHTML = i; 
    frag.appendChild(el); 

document.body.appendChild(frag); 
服用后: 
var html = []; 
for (var i = 0; i < 1000; i++) { 
    html.push('<p>' + i + '</p>'); 

document.body.innerHTML = html.join(''); 
1.4 使用firstChild和nextSibling代替childNodes遍历dom元素 
说明:约能获得30%-50%的性能提高。逆向遍历时使用lastChild和previousSibling。 
服用前: 
var nodes = element.childNodes; 
for (var i = 0, l = nodes.length; i < l; i++) { 
var node = nodes[i]; 
…… 

服用后: 
var node = element.firstChild; 
while (node) { 
…… 
node = node.nextSibling; 

2. 字符串 
2.1 使用Array做为StringBuffer,代替字符串拼接的操作 
说明:IE在对字符串拼接的时候,会创建临时的String对象;经测试,在IE下,当拼接的字符串越来越大时,运行效率会急剧下降。Fx和Opera都对字符串拼接操作进行了优化;经测试,在Fx下,使用Array的join方式执行时间约为直接字符串拼接的1.4倍。 
服用前: 
var now = new Date(); 
var str = ''; 
for (var i = 0; i < 10000; i++) { 
    str += '123456789123456789'; 

alert(new Date() - now); 
服用后: 
var now = new Date(); 
var strBuffer = []; 
for (var i = 0; i < 10000; i++) { 
    strBuffer.push('123456789123456789'); 

var str = strBuffer.join(''); 
alert(new Date() - now); 
3. 循环语句 
3.1 将循环控制量保存到局部变量 
说明:对数组和列表对象的遍历时,提前将length保存到局部变量中,避免在循环的每一步重复取值。 
服用前: 
var list = document.getElementsByTagName('p'); 
for (var i = 0; i < list.length; i++) { 
    …… 

服用后: 
var list = document.getElementsByTagName('p'); 
for (var i = 0, l = list.length; i < l; i++) { 
    …… 

3.2 顺序无关的遍历时,用while替代for 
说明:该方法可以减少局部变量的使用。比起效率优化,更能直接看到的是字符数量的优化。该做法有程序员强迫症的嫌疑。 
服用前: 
var arr = [1,2,3,4,5,6,7]; 
var sum = 0; 
for (var i = 0, l = arr.length; i < l; i++) { 
    sum += arr[i]; 
}    
服用后: 
var arr = [1,2,3,4,5,6,7]; 
var sum = 0, l = arr.length; 
while (l--) { 
    sum += arr[l]; 

4. 条件分支 
4.1 将条件分支,按可能性顺序从高到低排列 
说明:可以减少解释器对条件的探测次数。 
4.2 在同一条件子的多(>2)条件分支时,使用switch优于if 
说明:switch分支选择的效率高于if,在IE下尤为明显。4分支的测试,IE下switch的执行时间约为if的一半。 
4.3 使用三目运算符替代条件分支 
服用前: 
if (a > b) { 
num = a; 
} else { 
num = b; 

服用后: 
num = a > b ? a : b; 
5. 定时器 
5.1 需要不断执行的时候,优先考虑使用setInterval 
说明:setTimeout每一次都会初始化一个定时器。setInterval只会在开始的时候初始化一个定时器 
服用前: 
var timeoutTimes = 0; 
function timeout () { 
    timeoutTimes++; 
    if (timeoutTimes < 10) { 
        setTimeout(timeout, 10); 
    } 

timeout(); 
服用后: 
var intervalTimes = 0; 
function interval () { 
    intervalTimes++; 
    if (intervalTimes >= 10) { 
        clearInterval(interv); 
    } 

var interv = setInterval(interval, 10); 
5.2 使用function而不是string 
说明:如果把字符串作为setTimeout和setInterval的参数,浏览器会先用这个字符串构建一个function。 
服用前: 
var num = 0; 
setTimeout('num++', 10);    
服用后: 
var num = 0; 
function addNum () { 
    num++; 

setTimeout(addNum, 10); 
6. 其他 
6.1 尽量不使用动态语法元素 
说明:eval、Function、execScript等语句会再次使用javascript解析引擎进行解析,需要消耗大量的执行时间。 
6.2 重复使用的调用结果,事先保存到局部变量 
说明:避免多次取值的调用开销。 
服用前: 
var h1 = element1.clientHeight + num1; 
var h2 = element1.clientHeight + num2; 
服用后: 
var eleHeight = element1.clientHeight; 
var h1 = eleHeight + num1; 
var h2 = eleHeight + num2; 
6.3 使用直接量 
说明: 
var a = new Array(param,param,...) -> var a = [] 
var foo = new Object() -> var foo = {} 
var reg = new RegExp() -> var reg = /.../ 
6.4 避免使用with 
说明: with虽然可以缩短代码量,但是会在运行时构造一个新的scope。 
OperaDev上还有这样的解释,使用with语句会使得解释器无法在语法解析阶段对代码进行优化。对此说法,无法验证。 
服用前: 
with (a.b.c.d) { 
property1 = 1; 
property2 = 2; 

服用后: 
var obj = a.b.c.d; 
obj.property1 = 1; 
obj.property2 = 2; 
6.5 巧用||和&&布尔运算符 
重要程度:★★★ 
服用前: 
function eventHandler (e) { 
if(!e) e = window.event; 

服用后: 
function eventHandler (e) { 
e = e || window.event; 

服用前: 
if (myobj) { 
doSomething(myobj); 

服用后: 
myobj && doSomething(myobj); 
6.6 类型转换 
说明: 
1).    数字转换成字符串,应用"" + 1,性能上:("" +) > String() > .toString() > new String(); 
2).    浮点数转换成整型,不使用parseInt(), parseInt()是用于将字符串转换成数字,而不是浮点数和整型之间的转换,建议使用Math.floor()或者Math.round() 
3).    对于自定义的对象,推荐显式调用toString()。内部操作在尝试所有可能性之后,会尝试对象的toString()方法尝试能否转化为String。 
二.内存管理 
2.1 循环引用 
说明:如果循环引用中包含DOM对象或者ActiveX对象,那么就会发生内存泄露。内存泄露的后果是在浏览器关闭前,即使是刷新页面,这部分内存不会被浏览器释放。 
简单的循环引用: 
var el = document.getElementById('MyElement'); 
var func = function () {…} 
el.func = func; 
func.element = el; 
但是通常不会出现这种情况。通常循环引用发生在为dom元素添加闭包作为expendo的时候。 
如: 
function init() { 
    var el = document.getElementById('MyElement'); 
el.onclick = function () {……} 

init(); 
init在执行的时候,当前上下文我们叫做context。这个时候,context引用了el,el引用了function,function引用了context。这时候形成了一个循环引用。 
下面2种方法可以解决循环引用: 
1)    置空dom对象 
服用前: 
function init() { 
var el = document.getElementById('MyElement'); 
el.onclick = function () {……} 

init(); 
服用后: 
function init() { 
var el = document.getElementById('MyElement'); 
el.onclick = function () {……} 
el = null; 

init(); 
将el置空,context中不包含对dom对象的引用,从而打断循环应用。 
如果我们需要将dom对象返回,可以用如下方法: 
服用前: 
function init() { 
    var el = document.getElementById('MyElement'); 
    el.onclick = function () {……} 
    return el; 

init(); 
服用后: 
function init() { 
var el = document.getElementById('MyElement'); 
el.onclick = function () {……} 
try{ 
return el; 
} finally { 
    el = null; 


init(); 
2)    构造新的context 
服用前: 
function init() { 
    var el = document.getElementById('MyElement'); 
    el.onclick = function () {……} 

init(); 
服用后: 
function elClickHandler() {……} 
function init() { 
    var el = document.getElementById('MyElement'); 
    el.onclick = elClickHandler; 

init(); 
把function抽到新的context中,这样,function的context就不包含对el的引用,从而打断循环引用。 
2.2 通过javascript创建的dom对象,必须append到页面中 
说明:IE下,脚本创建的dom对象,如果没有append到页面中,刷新页面,这部分内存是不会回收的! 
示例代码: 
    function create () { 
        var gc = document.getElementById('GC'); 
        for (var i = 0; i < 5000 ; i++) 
        { 
            var el = document.createElement('div'); 
            el.innerHTML = "test"; 
            //下面这句可以注释掉,看看浏览器在任务管理器中,点击按钮然后刷新后的内存变化 
            gc.appendChild(el); 
        } 
    } 
2.3 释放dom元素占用的内存 
说明: 
将dom元素的innerHTML设置为空字符串,可以释放其子元素占用的内存。 
在rich应用中,用户也许会在一个页面上停留很长时间,可以使用该方法释放积累得越来越多的dom元素使用的内存。 
2.4 释放javascript对象 
说明:在rich应用中,随着实例化对象数量的增加,内存消耗会越来越大。所以应当及时释放对对象的引用,让GC能够回收这些内存控件。 
对象:obj = null 
对象属性:delete obj.myproperty 
数组item:使用数组的splice方法释放数组中不用的item 
2.5 避免string的隐式装箱 
说明:对string的方法调用,比如'xxx'.length,浏览器会进行一个隐式的装箱操作,将字符串先转换成一个String对象。推荐对声明有可能使用String实例方法的字符串时,采用如下写法: 
var myString = new String('Hello World');

转载于:https://my.oschina.net/jsan/blog/31980

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值