前端性能优化(2)---JS优化

JS性能优化的主要方式有:

1、避免不必要的DOM操作

浏览器遍历元素的代价是昂贵的,最简单的优化DOM树的查询方案是,当一个元素出现多次的时候,可以将它保存在一个变量中,这样便可以避免多次查询DOM树了。

// 推荐
var myList = "";
var myListHTML = document.getElementById("myList").innerHTML;

for (var i = 0; i < 100; i++) {
  myList += "" + i + "";
}

myListHTML = myList;

// 不推荐
for (var i = 0; i < 100; i++) {
  document.getElementById("myList").innerHTML += "" + i + "";
}
2、缓存数组的长度

循环的操作和Javascript性能非常相关的一部分,通过缓存数组的长度,可以有效地避免每次循环重新计算。

var arr = new Array(1000), len = arr.length,  i;
// 推荐 --数组的长度仅计算一次,然后被缓存
for (i = 0,i < len; i++) {
    ...
}
//不推荐 --数组的长度需要计算1000次
for (i = 0; i < arr.length; i++) {
    ...
}
3、不要再循环中创建函数

没创建一个函数对象是需要占用大量的空间的,所以在一个循环中创建函数是一个不理智的做法,尽量将函数移动到循环之前创建。

//推荐
var data = []
var m = data.length;
var handler = function(data){
    //do something
};
for(var i = 0;i < m; i++) {
    handlerData(data[i], handler);
}

//不推荐
var data = []
for(var i = 0, m = data.length; i < m; i++) {
    handlerData(data[i], function(data){
        //do something
    });
}
4、利用垃圾回收机制回收不再需要的对象

V8引擎中的垃圾回收机制可以判断保存在内存里的对象是活对象还是死对象,判断方法是按照是否有根对象或者活对象含有对它的引用来判断的。若是有根对象或者活对象引用了该对象,则被判定为活对象。所以有时候需要我们手动消除这些引用来通过垃圾回收机制来回收这些对象。

  • 全局变量
    在垃圾回收机制中,根对象被认为是活对象,因此全局对象也就是根对象,所以,在全局作用域中的变量会一直存在,不能被回收。
  • 闭包
    闭包中内部函数引用的变量和形参不会被垃圾回收机制所回收,使得一个变量常驻内存。这样虽然避免了全局变量的污染,但是同时也容易导致内存的泄露。
手动消除不能被垃圾回收机制所释放的对象的主要方法有:
  • delete
    通过delete的方式看可以消除对象中的键值对,从而消除对象的引用,但是对于var声明的全局变量,并不能通过delete操作符进行删除。
//可删除
var o = { x: 1 };
delete o.x; // true
o.x; // undefined
//不可删除
var x = 1;
delete x; // false
x; // 1
---------------------->
function x(){}
delete x; // false
typeof x; // "function"
  • null
    通过将变量或对象的属性设置为null,也可以消除引用。null使得原来引用的对象为空,然后再垃圾回收的时候进行消除。这种方式不会改变对象的结构,比delete要好用。
5、慎用eval()和with()
  • eval()
    eval()函数可以接受一个字符串str作为参数,并把这个参数作为脚本代码来执行。参数情况:(1)如参数是一个表达式则执行表达式;(2)若参数是Javascript语句,则执行其中的Javascript代码。
eval("var a=1");//声明一个变量a并赋值1。
eval("2+3");//执行加运算,并返回运算值。
eval("mytest()");//执行mytest()函数。
eval("{b:2}");//声明一个对象。如果想返回此对象,则需要在对象外面再嵌套一层小括如下:eval("({b:2})"); 

eval()函数再运行时需要条用解析引擎对其内部的字符串进行解释运行,需要消耗大量的内存。

  • with()
    with对象能够使我们很方便的使用某个对象的一些属性,而不用每次都去写对象名.属性 的形式,直接使用对象名。
//使用with对象时
function validate_email(field, alerttxt) {    
    with(field) {
        apos = value.indexOf("@")
        dotpos = value.lastIndexOf(".")
        if(apos < 1 || dotpos - apos < 2)  {
            alert(alerttxt);
            return false
        } else {
            return true
        }
    }
}
//不使用with对象时
function validate_email(field,alerttxt){
    field.apos=value.indexOf("@")
    field.dotpos=value.lastIndexOf(".")
    if (field.apos<1 || field.dotpos-field.apos<2) {
          alert(alerttxt);
          return false
    }else {
          return true
    }
}

通过with包裹的代码块,作用域链将会额外增加一层,降低索引效率,故不建议使用。

6、异步加载第三方内容

当我们无法保证嵌入的第三方内容是否可以正常工作时,可以考虑使用异步加载这些代码,避免阻塞整个页面的加载。
常见的异步加载的方式:

  • async属性
    利用html5的新属性async;

  • JS异步加载
    这种方法是监听页面加载完之后,在页面中加入script,从而达到引入js文件。
(function() {
    function asyncLoad() {
        var src = "http://thirdpart/js.js";
        var urls = src.split(",");
        var len = urls.length
        var x = document.getElementsByTagName('body');
        if(x && x[0]){
            for (var i = 0; i < len; i++) {
                  var s = document.createElement('script');
                      s.type = 'text/javascript';
                      s.async = true;
                      s.src = urls[i];
                      x[0].appendChild(s);
             }
         }
    }
    window.attachEvent ? 
    window.attachEvent('onload', asyncLoad) :
    window.addEventListener('load', asyncLoad, false);
})();
参考文档:

前端开发规范

你可能感兴趣的:(前端性能优化(2)---JS优化)