• javascript 代码效率优化-重要


      JS代码的执行效率往往直接影响了页面的性能,有的时候,实现同样的功能,不同的JS代码往往在效率上相差很多,有的时候仅仅是由于我们的书写习惯导致的,当然在高级点的浏览器中,它们大多都已经帮我们优化了,但...

      JS代码的执行效率往往直接影响了页面的性能,有的时候,实现同样的功能,不同的JS代码往往在效率上相差很多,有的时候仅仅是由于我们的书写习惯导致的,当然在高级点的浏览器中,它们大多都已经帮我们优化了,但是在中国,万恶的IE6仍然大量的存在,我们不得不去考虑它。对于JS代码的优化,实际上有很多的情况,有些影响是比较小的,而有些是比较严重的,本文中,我把几个我认为影响比较严重的情况列出来,供大家参考。 

    一、字符串的拼接

      字符串的拼接在我们开发中会经常遇到,所以我把其放在首位,我们往往习惯的直接用+=的方式来拼接字符串,其实这种拼接的方式效率非常的低,我们可以用一种巧妙的方法来实现字符串的拼接,那就是利用数组的join方法。

     1 <div class="one" id="one"></div>
     2 <input type="button" value="效率低" onclick="func1()" />
     3 <input type="button" value="效率高" onclick="func2()" />
     4 
     5 <script>
     6     //效率低的
     7     function func1(){
     8         var start = new Date().getTime();
     9         var template = "";
    10         for(var i = 0; i < 10000; i++){
    11             template += "<input type='button' value='a'>";
    12         }
    13         var end = new Date().getTime();
    14         document.getElementById("one").innerHTML = template;
    15         alert("用时:" + (end - start) + "毫秒");
    16     }
    17 
    18     //效率高的
    19     function func2(){
    20         var start = new Date().getTime();
    21         var array = [];
    22         for(var i = 0; i < 10000; i++){
    23             array[i] = "<input type='button' value='a'>";
    24         }
    25         var end = new Date().getTime();
    26         document.getElementById("one").innerHTML = array.join("");
    27         alert("用时:" + (end - start) + "毫秒");
    28     }
    29 </script>

     

      我们看看其在不同浏览器下执行的情况

      我们会发现,在IE6下其差别是相当明显的,其实这种情况在IE的高版本中体现的也非常明显,但是在Firefox下却没有多大的区别,相反第二种的相对效率还要低点,不过只是差别2ms左右,而Chrome也和Firefox类似。另外在这里顺便说明一下,在我们给数组添加元素的时候,很多人喜欢用数组的原生的方法push,其实直接用arr[i]或者arr[arr.length]的方式要快一点,大概在10000次循环的情况IE浏览器下会有十几毫秒的差别。

    二、for循环

      for循环是我们经常会遇到的情况,我们先看看下面例子: 

     1 <input type="button" value="效率低" onclick="func1()" />
     2 <input type="button" value="效率高" onclick="func2()" />
     3 <script>
     4     var arr = [];
     5     for(var i = 0; i < 10000; i++){
     6         arr[i] = "<div>" + i + "</div>";
     7     }
     8     document.body.innerHTML += arr.join("");
     9 
    10     //效率低的
    11     function func1(){
    12         var divs = document.getElementsByTagName("div");
    13         var start = new Date().getTime();
    14         for(var i = 0; i < divs.length; i++){
    15             //"效率低"
    16         }
    17         var end = new Date().getTime();
    18         alert("用时:" + (end - start) + "毫秒");
    19     }
    20 
    21     //效率高的
    22     function func2(){
    23         var divs = document.getElementsByTagName("div");
    24         var start = new Date().getTime();
    25         for(var i = 0, len = divs.length; i < len; i++){
    26             //"效率高"
    27         }
    28         var end = new Date().getTime();
    29         alert("用时:" + (end - start) + "毫秒");
    30     }
    31 </script>

     

     

      由上表可以看出,在IE6.0下,其差别是非常明显,而在Firefox和Chrome下几乎没有差别,之所以在IE6.0下会有这种情况,主要是因为for循环在执行中,第一种情况会每次都计算一下长度,而第二种情况却是在开始的时候计算长度,并把其保存到一个变量中,所以其执行效率要高点,所以在我们使用for循环的时候,特别是需要计算长度的情况,我们应该开始将其保存到一个变量中。但是并不是只要是取长度都会出现如此明显的差别,如果我们仅仅是操作一个数组,取得的是一个数组的长度,那么其实两种方式的写法都差不多,我们看下面的例子:

     1 <input type="button" value="效率低" onclick="func1()" />
     2 <input type="button" value="效率高" onclick="func2()" />
     3 <script>
     4     var arr2 = [];
     5     for(var i = 0; i < 10000; i++){
     6         arr2[i] = "<div>" + i + "</div>";
     7     }
     8 
     9     //效率低的
    10     function func1(){
    11         var start = new Date().getTime();
    12         for(var i = 0; i < arr2.length; i++){
    13             //"效率低"
    14         }
    15         var end = new Date().getTime();
    16         alert("用时:" + (end - start) + "毫秒");
    17     }
    18 
    19     //效率高的
    20     function func2(){
    21         var start = new Date().getTime();
    22         for(var i = 0, len = arr2.length; i < len; i++){
    23             //"效率高"
    24         }
    25         var end = new Date().getTime();
    26         alert("用时:" + (end - start) + "毫秒");
    27     }
    28 </script>

      从上表可以看出,如果仅仅是一个数组的话,我们看到其实两种写法都是差不多的,其实如果我们把循环再上调到100000次的话,也仅仅是差别几毫秒而已,所以在数组的情况下,我认为都是一样的。对于for循环的优化,也有人提出很多点,有人认为用-=1,或者从大到小的方式循环等等,我认为是完全没有必要的,这些优化往往实际情况下根本没有表现出来,换句话说只是计算机级别的微小的变化,但是给我们带来的却是代码的可读性大大的降低,所以实在是得不偿失。

    三、减少页面的重绘

      减少页面重绘虽然本质不是JS本身的优化,但是其往往是由JS引起的,而重绘的情况往往是严重影响页面性能的,所以完全有必要拿出来,我们看下面例子

     1 <div id="demo"></div>
     2 <input type="button" value="效率低" onclick="func1()" />
     3 <input type="button" value="效率高" onclick="func2()" />
     4 
     5 <script>
     6     var str = "<div>这是一个测试字符串</div><div>这是一个测试字符串</div><div>这是一个测试字符串</div><div>这是一个测试字符串</div><div>这是一个测试字符串</div><div>这是一个测试字符串</div><div>这是一个测试字符串</div><div>这是一个测试字符串</div><div>这是一个测试字符串</div><div>这是一个测试字符串</div><div>这是一个测试字符串</div><div>这是一个测试字符串</div><div>这是一个测试字符串</div>";
     7 
     8     //效率低的
     9     function func1(){
    10         var obj = document.getElementById("demo");
    11         var start = new Date().getTime();
    12         for(var i = 0; i < 100; i++){
    13             obj.innerHTML += str + i;
    14         }
    15         var end = new Date().getTime();
    16         alert("用时 " + (end - start) + " 毫秒");
    17     }
    18 
    19     //效率高的
    20     function func2(){
    21         var obj = document.getElementById("demo");
    22         var start = new Date().getTime();
    23         var arr = [];
    24         for(var i = 0; i < 100; i++){
    25             arr[i] = str + i;
    26         }
    27         obj.innerHTML = arr.join("");
    28         var end = new Date().getTime();
    29         alert("用时 " + (end - start) + " 毫秒");
    30     }
    31 </script>

      在例子中,我只是用了100次的循环,因为如果用10000次循环的话,浏览器基本上就卡住不动了,但是即使是100次的循环,我们看看下面的执行结果。

      可以看到的是,这简直是一个惊人的结果,仅仅100次的循环,不管是在什么浏览器下,都出现了如此之大的差别,另外我们还发现,在这里,IE6的执行效率居然比起Firefox还要好很多,可见Firefox在页面重绘这方面并没有做一些的优化。这里还要注意的是,一般影响页面重绘的不仅仅是innerHTML,如果改变元素的样式,位置等情况都会触发页面重绘,所以在平时一定要注意这点。

    四、减少作用域链上的查找次数

      我们知道,js代码在执行的时候,如果需要访问一个变量或者一个函数的时候,它需要遍历当前执行环境的作用域链,而遍历是从这个作用域链的前端一级一级的向后遍历,直到全局执行环境,所以这里往往会出现一个情况,那就是如果我们需要经常访问全局环境的变量对象的时候,我们每次都必须在当前作用域链上一级一级的遍历,这显然是比较耗时的,我们看下面的例子: 

     1 <div id="demo"></div>
     2 <input id="but1" type="button" onclick="func1()" value="效率低"/>
     3 <input id="but2" type="button" onclick="func2()" value="效率高"/>
     4 
     5 <script>
     6     function func1(){
     7         var start = new Date().getTime();
     8         for(var i = 0; i < 10000; i++){
     9             var but1 = document.getElementById("but1");
    10             var but2 = document.getElementById("but2");
    11             var inputs = document.getElementsByTagName("input");
    12             var divs = document.getElementsByTagName("div");
    13             var but1 = document.getElementById("but1");
    14             var but2 = document.getElementById("but2");
    15             var inputs = document.getElementsByTagName("input");
    16             var divs = document.getElementsByTagName("div");
    17             var but1 = document.getElementById("but1");
    18             var but2 = document.getElementById("but2");
    19             var inputs = document.getElementsByTagName("input");
    20             var divs = document.getElementsByTagName("div");
    21             var but1 = document.getElementById("but1");
    22             var but2 = document.getElementById("but2");
    23             var inputs = document.getElementsByTagName("input");
    24             var divs = document.getElementsByTagName("div");
    25             var but1 = document.getElementById("but1");
    26             var but2 = document.getElementById("but2");
    27             var inputs = document.getElementsByTagName("input");
    28             var divs = document.getElementsByTagName("div");
    29             var but1 = document.getElementById("but1");
    30             var but2 = document.getElementById("but2");
    31             var inputs = document.getElementsByTagName("input");
    32             var divs = document.getElementsByTagName("div");
    33         }
    34         var end = new Date().getTime();
    35         alert("用时 " + (end - start) + " 毫秒");
    36     }
    37 
    38     function func2(){
    39         var start = new Date().getTime();
    40         var doc = document;
    41         for(var i = 0; i < 10000; i++){
    42             var but1 = doc.getElementById("but1");
    43             var but2 = doc.getElementById("but2");
    44             var inputs = doc.getElementsByTagName("input");
    45             var divs = doc.getElementsByTagName("div");
    46             var but1 = doc.getElementById("but1");
    47             var but2 = doc.getElementById("but2");
    48             var inputs = doc.getElementsByTagName("input");
    49             var divs = doc.getElementsByTagName("div");
    50             var but1 = doc.getElementById("but1");
    51             var but2 = doc.getElementById("but2");
    52             var inputs = doc.getElementsByTagName("input");
    53             var divs = doc.getElementsByTagName("div");
    54             var but1 = doc.getElementById("but1");
    55             var but2 = doc.getElementById("but2");
    56             var inputs = doc.getElementsByTagName("input");
    57             var divs = doc.getElementsByTagName("div");
    58             var but1 = doc.getElementById("but1");
    59             var but2 = doc.getElementById("but2");
    60             var inputs = doc.getElementsByTagName("input");
    61             var divs = doc.getElementsByTagName("div");
    62             var but1 = doc.getElementById("but1");
    63             var but2 = doc.getElementById("but2");
    64             var inputs = doc.getElementsByTagName("input");
    65             var divs = doc.getElementsByTagName("div");
    66         }
    67         var end = new Date().getTime();
    68         alert("用时 " + (end - start) + " 毫秒");
    69     }
    70 </script>

       上面代码中,第二种情况是先把全局对象的变量放到函数里面先保存下来,然后直接访问这个变量,而第一种情况是每次都遍历作用域链,直到全局环境,我们看到第二种情况实际上只遍历了一次,而第一种情况却是每次都遍历了,所以我们看看其执行结果:

      从上表中可以看出,其在IE6下差别还是非常明显的,而且这种差别在多级作用域链和多个全局变量的情况下还会表现的非常明显。

    五、避免双重解释

      双重解释的情况也是我们经常会碰到的,有的时候我们没怎么考虑到这种情况会影响到效率,双重解释一般在我们使用eval、new Function和setTimeout等情况下会遇到,我们看看下面的例子:

     1 <div id="demo"></div>
     2 <input id="but1" type="button" onclick="func1()" value="效率低">
     3 <input id="but2" type="button" onclick="func2()" value="效率高">
     4 
     5 <script>
     6     var sum, num1 = 1, num2 = 2;
     7     function func1(){
     8         var start = new Date().getTime();
     9         for(var i = 0; i < 10000; i++){
    10             var func = new Function("sum+=num1;num1+=num2;num2++;");
    11             func();
    12         }
    13         var end = new Date().getTime();
    14         alert("用时 " + (end - start) + " 毫秒");
    15     }
    16     
    17     function func2(){
    18         var start = new Date().getTime();
    19         for(var i = 0; i < 10000; i++){
    20             sum+=num1;
    21             num1+=num2;
    22             num2++;
    23         }
    24         var end = new Date().getTime();
    25         alert("用时 " + (end - start) + " 毫秒");
    26     }
    27 </script>

      第一种情况我们是使用了new Function来进行双重解释,而第二种是避免了双重解释,我们看看在不同浏览器下的表现:

      可以看到,在所有的浏览器中,双重解释都是有很大开销的,所以在实际当中要尽量避免双重解释。

      感谢"SeaSunK"对第四点测试报告错误的指正,现在已经修改过来了。至于最后一点提出的func1每次都初始化,没有可比性,所以我给换了eval,结果发现,在IE6.0下还是有影响,而且在Firefox下,使用eval对效率的影响程度更加厉害,在Firefox下,如果10000次循环,需要十多秒的时间,所以我把循环都变成了1000次。看代码和报告

     1 <div id="demo"></div>
     2 <input id="but1" type="button" onclick="func1()" value="效率低">
     3 <input id="but2" type="button" onclick="func2()" value="效率高">
     4 <script>
     5     var sum, num1 = 1, num2 = 2;
     6     function func1(){
     7         var start = new Date().getTime();
     8         for(var i = 0; i < 1000; i++){
     9             eval("sum+=num1;num1+=num2;num2++;");
    10         }
    11         var end = new Date().getTime();
    12         alert("用时 " + (end - start) + " 毫秒");
    13     }
    14     function func2(){
    15         var start = new Date().getTime();
    16         for(var i = 0; i < 1000; i++){
    17             sum+=num1;
    18             num1+=num2;
    19             num2++;
    20         }
    21         var end = new Date().getTime();
    22         alert("用时 " + (end - start) + " 毫秒");
    23     }
    24 </script>

    六、DOM执行效率

    1.1 使用DocumentFragment优化多次append,使用文档碎片

    说明:添加多个dom元素时,先将元素append到DocumentFragment中,最后统一将DocumentFragment添加到页面。

    该做法可以减少页面渲染dom元素的次数。经IE和Fx下测试,在append1000个元素时,效率能提高10%-30%,Fx下提升较为明显。

     1 <script>
     2     修改前:
     3     for (var i = 0; i < 1000; i++) {
     4         var el = document.createElement('p');
     5         el.innerHTML = i;
     6         document.body.appendChild(el);
     7     }
     8 
     9     修改后:
    10     var frag = document.createDocumentFragment();
    11     for (var i = 0; i < 1000; i++) {
    12         var el = document.createElement('p');
    13         el.innerHTML = i;
    14         frag.appendChild(el);
    15     }
    16 
    17     document.body.appendChild(frag);
    18 </script>

     

    1.2 通过模板元素clone,替代createElement

    说明:通过一个模板dom对象cloneNode,效率比直接创建element高。

    性能提高不明显,约为10%左右。在低于100个元素create和append操作时,没有优势。

     1 <script>
     2     服用前:
     3     var frag = document.createDocumentFragment();
     4     for (var i = 0; i < 1000; i++) {
     5         var el = document.createElement('p');
     6         el.innerHTML = i;
     7         frag.appendChild(el);
     8     }
     9 
    10     document.body.appendChild(frag);
    11 
    12     服用后:
    13     var frag = document.createDocumentFragment();
    14     var pEl = document.getElementsByTagName('p')[0];
    15     for (var i = 0; i < 1000; i++) {
    16         var el = pEl.cloneNode(false);
    17         el.innerHTML = i;
    18         frag.appendChild(el);
    19     }
    20 
    21     document.body.appendChild(frag);
    22 </script>

     

    1.3 使用一次innerHTML赋值代替构建dom元素

      说明:根据数据构建列表样式的时候,使用设置列表容器innerHTML的方式,比构建dom元素并append到页面中的方式,效率有数量级上的提高。

     1 <script>
     2     服用前:
     3     var frag = document.createDocumentFragment();
     4     for (var i = 0; i < 1000; i++) {
     5         var el = document.createElement('p');
     6         el.innerHTML = i;
     7         frag.appendChild(el);
     8     }
     9 
    10     document.body.appendChild(frag);
    11 
    12     服用后:
    13     var html = [];
    14     for (var i = 0; i < 1000; i++) {
    15         html.push('<p>' + i + '</p>');
    16     }
    17 
    18     document.body.innerHTML = html.join('');
    19 </script>

     

    1.4 使用firstChild和nextSibling代替childNodes遍历dom元素

    说明:约能获得30%-50%的性能提高。逆向遍历时使用lastChild和previousSibling。

     1 <script>
     2     // 服用前:
     3     var nodes = element.childNodes;
     4     for (var i = 0, l = nodes.length; i < l; i++) {
     5         var node = nodes;
     6              ……
     7     }
     8 
     9     // 服用后:
    10     var node = element.firstChild;
    11     while (node) {
    12     ……
    13      node = node.nextSibling;
    14     }
    15 </script>

     

    七、字符串执行效率

    2.1 使用Array做为StringBuffer,代替字符串拼接的操作

      说明:IE在对字符串拼接的时候,会创建临时的String对象;经测试,在IE下,当拼接的字符串越来越大时,运行效率会急剧下降。Fx和Opera都对字符串拼接操作进行了优化;经测试,在Fx下,使用Array的join方式执行时间约为直接字符串拼接的1.4倍。

     1 <script>
     2     // 服用前:
     3     var now = new Date();
     4     var str = '';
     5     for (var i = 0; i < 10000; i++) {
     6         str += '123456789123456789';
     7     }
     8     alert(new Date() - now);
     9 
    10     // 服用后:
    11     var now = new Date();
    12     var strBuffer = [];
    13     for (var i = 0; i < 10000; i++) {
    14         strBuffer.push('123456789123456789');
    15     }
    16     var str = strBuffer.join('');
    17     alert(new Date() - now);
    18 </script>

     

    八、循环语句执行效率

    3.1 将循环控制量保存到局部变量

      说明:对数组和列表对象的遍历时,提前将length保存到局部变量中,避免在循环的每一步重复取值。

     1 <script>
     2     // 服用前:
     3     var list = document.getElementsByTagName('p');
     4     for (var i = 0; i < list.length; i++) {
     5     ……
     6     }
     7 
     8     // 服用后:
     9     var list = document.getElementsByTagName('p');
    10     for (var i = 0, l = list.length; i < l; i++) {
    11     ……
    12     }
    13 </script>

     

    3.2 顺序无关的遍历时,用while替代for

    说明:该方法可以减少局部变量的使用。比起效率优化,更能直接看到的是字符数量的优化。该做法有程序员强迫症的嫌疑。

     1 <script>
     2     // 服用前:
     3     var arr = [1,2,3,4,5,6,7];
     4     var sum = 0;
     5     for (var i = 0, l = arr.length; i &lt; l; i++) {
     6         sum += arr;
     7     }
     8     
     9     // 服用后:
    10     var arr = [1,2,3,4,5,6,7];
    11     var sum = 0, l = arr.length;
    12     while (l--) {
    13         sum += arr[l];
    14     }
    15 </script>

     

    九、条件分支执行效率

    4.1 将条件分支,按可能性顺序从高到低排列

    说明:可以减少解释器对条件的探测次数。

    4.2 在同一条件子的多(>2)条件分支时,使用switch优于if

    说明:switch分支选择的效率高于if,在IE下尤为明显。4分支的测试,IE下switch的执行时间约为if的一半。

    4.3 使用三目运算符替代条件分支

     1 <script>
     2     // 服用前:
     3     if (a > b) {
     4         num = a;
     5     }
     6     else {
     7         num = b;
     8     }
     9     9
    10     // 服用后:
    11     num = a > b ? a : b;
    12 </script>

    十、定时器执行效率

    5.1 需要不断执行的时候,优先考虑使用setInterval

      说明:setTimeout每一次都会初始化一个定时器。setInterval只会在开始的时候初始化一个定时器

     1 <script>
     2     //服用前:
     3     var timeoutTimes = 0;
     4     function timeout () {
     5         timeoutTimes++;
     6         if (timeoutTimes < 10) {
     7             setTimeout(timeout, 10);
     8         }
     9     }
    10     
    11     timeout();
    12     
    13     //服用后:
    14     var intervalTimes = 0;
    15     function interval () {
    16         intervalTimes++;
    17         if (intervalTimes >= 10) {
    18             clearInterval(interv);
    19         }
    20     }
    21     
    22     var interv = setInterval(interval, 10);
    23 </script>

     

    5.2 使用function而不是string

    说明:如果把字符串作为setTimeout和setInterval的参数,浏览器会先用这个字符串构建一个function。

     1 <script>
     2     // 服用前:
     3     var num = 0;
     4     setTimeout('num++', 10);
     5     
     6     // 服用后:
     7     var num = 0;
     8     function addNum () {
     9         num++;
    10     }
    11     setTimeout(addNum, 10);
    12 </script>

     

    十一、其他执行效率

    6.1 尽量不使用动态语法元素

    说明:eval、Function、execScript等语句会再次使用javascript解析引擎进行解析,需要消耗大量的执行时间。

    6.2 重复使用的调用结果,事先保存到局部变量

    说明:避免多次取值的调用开销。

     1 <script>
     2     // 服用前:
     3     var h1 = element1.clientHeight + num1;
     4     var h2 = element1.clientHeight + num2;
     5     
     6     // 服用后:
     7     var eleHeight = element1.clientHeight;
     8     var h1 = eleHeight + num1;
     9     var h2 = eleHeight + num2;
    10 </script>

     

    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语句会使得解释器无法在语法解析阶段对代码进行优化。对此说法,无法验证。

     1 <script>
     2     // 服用前:
     3     with (a.b.c.d) {
     4         property1 = 1;
     5         property2 = 2;
     6     }
     7     
     8     // 服用后:
     9     var obj = a.b.c.d;
    10     obj.property1 = 1;
    11     obj.property2 = 2;
    12 </script>

    6.5 巧用||和&&布尔运算符

    重要程度:★★★

     1 <script>
     2     // 服用前:
     3     function eventHandler (e) {
     4         if(!e) e = window.event;
     5     }
     6     
     7     // 服用后:
     8     function eventHandler (e) {
     9         e = e || window.event;
    10     }
    11     
    12     // 服用前:
    13     if (myobj) {
    14         doSomething(myobj);
    15     }
    16     
    17     // 服用后:
    18     myobj && doSomething(myobj)
    19 </script>

     

    6.6 类型转换

    说明:

    1).    数字转换成字符串,应用"" + 1,性能上:("" +) > String() > .toString() > new String();

    2).    浮点数转换成整型,不使用parseInt(), parseInt()是用于将字符串转换成数字,而不是浮点数和整型之间的转换,建议使用Math.floor()或者Math.round()

    3).    对于自定义的对象,推荐显式调用toString()。内部操作在尝试所有可能性之后,会尝试对象的toString()方法尝试能否转化为String。

    十二、内存管理

    2.1 循环引用

      说明:如果循环引用中包含DOM对象或者ActiveX对象,那么就会发生内存泄露。内存泄露的后果是在浏览器关闭前,即使是刷新页面,这部分内存不会被浏览器释放。

     1 <script>
     2     // 简单的循环引用:   
     3     var el = document.getElementById('MyElement');
     4     var func = function () {…}
     5     el.func = func;
     6     func.element = el;
     7     
     8     // 但是通常不会出现这种情况。通常循环引用发生在为dom元素添加闭包作为expendo的时候。    
     9     // 如:
    10     function init() {
    11         var el = document.getElementById('MyElement');
    12         el.onclick = function () {……}
    13     }
    14     init();
    15 </script>

      init在执行的时候,当前上下文我们叫做context。这个时候,context引用了el,el引用了function,function引用了context。这时候形成了一个循环引用。

    下面2种方法可以解决循环引用:

    1)    置空dom对象

     1 <script>
     2     // 服用前:
     3     function init() {
     4         var el = document.getElementById('MyElement');
     5         el.onclick = function () {……}
     6     }
     7 
     8     init();
     9 
    10     // 服用后:
    11     function init() {
    12         var el = document.getElementById('MyElement');
    13         el.onclick = function () {……}
    14         el = null;
    15     }
    16  
    17     init();
    18 
    19     将el置空,context中不包含对dom对象的引用,从而打断循环应用。
    20     如果我们需要将dom对象返回,可以用如下方法:
    21     // 服用前:
    22     function init() {
    23         var el = document.getElementById('MyElement');
    24         el.onclick = function () {……}
    25         return el;
    26     }
    27 
    28     init();
    29 
    30     // 服用后:
    31     function init() {
    32         var el = document.getElementById('MyElement');
    33         el.onclick = function () {……}
    34         try{
    35             return el;
    36         }
    37         finally {
    38             el = null;
    39         }
    40     }
    41 
    42     init();
    43 </script>

     

    2)    构造新的context

     1 <script>
     2     // 服用前:
     3     function init() {
     4         var el = document.getElementById('MyElement');
     5         el.onclick = function () {……}
     6     }
     7     
     8     init();
     9     
    10     // 服用后:
    11     function elClickHandler() {……}
    12     function init() {
    13         var el = document.getElementById('MyElement');
    14         el.onclick = elClickHandler;
    15     }
    16     
    17     init();
    18     
    19     把function抽到新的context中,这样,function的context就不包含对el的引用,从而打断循环引用。
    20 </script>

     

    2.2 通过javascript创建的dom对象,必须append到页面中

    说明:IE下,脚本创建的dom对象,如果没有append到页面中,刷新页面,这部分内存是不会回收的!

     1 <script>
     2     // 示例代码:
     3     function create () {
     4         var gc = document.getElementById('GC');
     5         for (var i = 0; i <5000; i++) {
     6             var el = document.createElement('div');
     7             el.innerHTML = "test";
     8             // 下面这句可以注释掉,看看浏览器在任务管理器中,点击按钮然后刷新后的内存变化
     9             gc.appendChild(el);
    10         }
    11     }
    12 </script>

     

    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');

  • 相关阅读:
    arthas Java诊断工具
    tomcat结构 请求处理过程 和 常见参数
    Dubbo 测试用例 获取注册中心的其他服务
    Vue3 父子组件传值,defineProps,defineEmits,以及父组件调用子组件的方法 defineExpose
    Snowflake
    js中Array和Object的keys(),values()和entries()方法
    深度拷贝
    deepin系统解决增删改文件没有变化
    文献的类别与引用方式
    读万卷书不如行万里路
  • 原文地址:https://www.cnblogs.com/couxiaozi1983/p/3510891.html
Copyright © 2020-2023  润新知