精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

看完離編寫高性能的JavaScript又近了一步

開發 前端
內存泄漏指由于疏忽或錯誤造成程序未能釋放已經不再使用的內存。內存泄漏并非指內存在物理上的消失,而是應用程序分配某段內存后,由于設計錯誤,導致在釋放該段內存之前就失去了對該段內存的控制,從而造成了內存的浪費。

 

[[212466]]

什么是內存泄露

內存泄漏指由于疏忽或錯誤造成程序未能釋放已經不再使用的內存。內存泄漏并非指內存在物理上的消失,而是應用程序分配某段內存后,由于設計錯誤,導致在釋放該段內存之前就失去了對該段內存的控制,從而造成了內存的浪費。

內存泄漏通常情況下只能由獲得程序源代碼的程序員才能分析出來。然而,有不少人習慣于把任何不需要的內存使用的增加描述為內存泄漏,即使嚴格意義上來說這是不準確的。

 ————wikipedia

意外的全局變量

JavaScript對未聲明變量的處理方式:在全局對象上創建該變量的引用(即全局對象上的屬性,不是變量,因為它能通過delete刪除)。如果在瀏覽器中,全局對象就是window對象。

如果未聲明的變量緩存大量的數據,會導致這些數據只有在窗口關閉或重新刷新頁面時才能被釋放。這樣會造成意外的內存泄漏。

  1. function foo(arg) { 
  2.  
  3.     bar = "this is a hidden global variable with a large of data"
  4.  

等同于:

  1. function foo(arg) { 
  2.  
  3.     window.bar = "this is an explicit global variable with a large of data"
  4.  

另外,通過this創建意外的全局變量: 

  1. function foo() { 
  2.  
  3.     this.variable = "potential accidental global"
  4.  
  5.  
  6. // 當在全局作用域中調用foo函數,此時this指向的是全局對象(window),而不是'undefined' 
  7.  
  8. foo(); 

解決方法:

在JavaScript文件中添加'use strict',開啟嚴格模式,可以有效地避免上述問題。

  1. function foo(arg) { 
  2.  
  3.     "use strict" // 在foo函數作用域內開啟嚴格模式 
  4.  
  5.     bar = "this is an explicit global variable with a large of data";// 報錯:因為bar還沒有被聲明 
  6.  

如果需要在一個函數中使用全局變量,可以像如下代碼所示,在window上明確聲明:

  1. function foo(arg) { 
  2.  
  3.     "use strict" // 在foo函數作用域內開啟嚴格模式 
  4.  
  5.     bar = "this is an explicit global variable with a large of data";// 報錯:因為bar還沒有被聲明 
  6.  

這樣不僅可讀性高,而且后期維護也方便

談到全局變量,需要注意那些用來臨時存儲大量數據的全局變量,確保在處理完這些數據后將其設置為null或重新賦值。全局變量也常用來做cache,一般cache都是為了性能優化才用到的,為了性能,***對cache的大小做個上限限制。因為cache是不能被回收的,越高cache會導致越高的內存消耗。

console.log

console.log:向web開發控制臺打印一條消息,常用來在開發時調試分析。有時在開發時,需要打印一些對象信息,但發布時卻忘記去掉console.log語句,這可能造成內存泄露。

在傳遞給console.log的對象是不能被垃圾回收 ♻️,因為在代碼運行之后需要在開發工具能查看對象信息。所以***不要在生產環境中console.log任何對象。

實例------>demos/log.html

  1. <!DOCTYPE html> 
  2. <html lang="en"
  3.  
  4. <head> 
  5.   <meta charset="UTF-8"
  6.   <meta name="viewport" content="width=device-width, initial-scale=1.0"
  7.   <meta http-equiv="X-UA-Compatible" content="ie=edge"
  8.   <title>Leaker</title> 
  9. </head> 
  10.  
  11. <body> 
  12.   <input type="button" value="click"
  13.   <script> 
  14.     !function () { 
  15.       function Leaker() { 
  16.         this.init(); 
  17.       }; 
  18.       Leaker.prototype = { 
  19.         init: function () { 
  20.           this.name = (Array(100000)).join('*'); 
  21.           console.log("Leaking an object %o: %o", (new Date()), this);// this對象不能被回收 
  22.         }, 
  23.  
  24.         destroy: function () { 
  25.           // do something.... 
  26.         } 
  27.       }; 
  28.       document.querySelector('input').addEventListener('click'function () { 
  29.         new Leaker(); 
  30.       }, false); 
  31.     }() 
  32.   </script> 
  33. </body> 
  34.  
  35. </html> 

這里結合Chrome的Devtools–>Performance做一些分析,操作步驟如下:

⚠️注:***在隱藏窗口中進行分析工作,避免瀏覽器插件影響分析結果

  1. 開啟【Performance】項的記錄
  2. 執行一次CG,創建基準參考線
  3. 連續單擊【click】按鈕三次,新建三個Leaker對象
  4. 執行一次CG
  5. 停止記錄

可以看出【JS Heap】線***沒有降回到基準參考線的位置,顯然存在沒有被回收的內存。如果將代碼修改為:

  1. !function () { 
  2.       function Leaker() { 
  3.         this.init(); 
  4.       }; 
  5.       Leaker.prototype = { 
  6.         init: function () { 
  7.           this.name = (Array(100000)).join('*'); 
  8.         }, 
  9.  
  10.         destroy: function () { 
  11.           // do something.... 
  12.         } 
  13.       }; 
  14.       document.querySelector('input').addEventListener('click'function () { 
  15.         new Leaker(); 
  16.       }, false); 
  17.     }() 

去掉console.log("Leaking an object %o: %o", (new Date()), this);語句。重復上述的操作步驟,分析結果如下:

從對比分析結果可知,console.log打印的對象是不會被垃圾回收器回收的。因此***不要在頁面中console.log任何大對象,這樣可能會影響頁面的整體性能,特別在生產環境中。除了console.log外,另外還有console.dir、console.error、console.warn等都存在類似的問題,這些細節需要特別的關注。

closures(閉包)

當一個函數A返回一個內聯函數B,即使函數A執行完,函數B也能訪問函數A作用域內的變量,這就是一個閉包——————本質上閉包是將函數內部和外部連接起來的一座橋梁。

  1. function foo(message) { 
  2.  
  3.     function closure() { 
  4.  
  5.         console.log(message) 
  6.  
  7.     }; 
  8.  
  9.     return closure; 
  10.  
  11.  
  12. // 使用 
  13.  
  14. var bar = foo("hello closure!"); 
  15.  
  16. bar()// 返回 'hello closure!' 

在函數foo內創建的函數closure對象是不能被回收掉的,因為它被全局變量bar引用,處于一直可訪問狀態。通過執行bar()可以打印出hello closure!。如果想釋放掉可以將bar = null即可。

由于閉包會攜帶包含它的函數的作用域,因此會比其他函數占用更多的內存。過度使用閉包可能會導致內存占用過多。

實例------>demos/closures.html

  1. <!DOCTYPE html> 
  2. <html lang="en"
  3.  
  4. <head> 
  5.   <meta charset="UTF-8"
  6.   <meta name="viewport" content="width=device-width, initial-scale=1.0"
  7.   <meta http-equiv="X-UA-Compatible" content="ie=edge"
  8.   <title>Closure</title> 
  9. </head> 
  10.  
  11. <body> 
  12.   <p>不斷單擊【click】按鈕</p> 
  13.   <button id="click_button">Click</button> 
  14.   <script> 
  15.     function f() { 
  16.       var str = Array(10000).join('#'); 
  17.       var foo = { 
  18.         name'foo' 
  19.       } 
  20.       function unused() { 
  21.         var message = 'it is only a test message'
  22.         str = 'unused: ' + str; 
  23.       } 
  24.       function getData() { 
  25.         return 'data'
  26.       } 
  27.       return getData; 
  28.     } 
  29.  
  30.     var list = []; 
  31.      
  32.     document.querySelector('#click_button').addEventListener('click'function () { 
  33.       list.push(f()); 
  34.     }, false); 
  35.   </script> 
  36. </body> 
  37.  
  38. </html> 

這里結合Chrome的Devtools->Memory工具進行分析,操作步驟如下:

⚠️注:***在隱藏窗口中進行分析工作,避免瀏覽器插件影響分析結果

  1. 選中【Record allocation timeline】選項
  2. 執行一次CG
  3. 單擊【start】按鈕開始記錄堆分析
  4. 連續單擊【click】按鈕十多次
  5. 停止記錄堆分析

上圖中藍色柱形條表示隨著時間新分配的內存。選中其中某條藍色柱形條,過濾出對應新分配的對象:

查看對象的詳細信息:

從圖可知,在返回的閉包作用鏈(Scopes)中攜帶有它所在函數的作用域,作用域中還包含一個str字段。而str字段并沒有在返回getData()中使用過。為什么會存在在作用域中,按理應該被GC回收掉, whyquestion

原因是在相同作用域內創建的多個內部函數對象是共享同一個變量對象(variable object)。如果創建的內部函數沒有被其他對象引用,不管內部函數是否引用外部函數的變量和函數,在外部函數執行完,對應變量對象便會被銷毀。反之,如果內部函數中存在有對外部函數變量或函數的訪問(可以不是被引用的內部函數),并且存在某個或多個內部函數被其他對象引用,那么就會形成閉包,外部函數的變量對象就會存在于閉包函數的作用域鏈中。這樣確保了閉包函數有權訪問外部函數的所有變量和函數。了解了問題產生的原因,便可以對癥下藥了。對代碼做如下修改:

 

  1. function f() { 
  2.  
  3.   var str = Array(10000).join('#'); 
  4.  
  5.   var foo = { 
  6.  
  7.     name'foo' 
  8.  
  9.   } 
  10.  
  11.   function unused() { 
  12.  
  13.     var message = 'it is only a test message'
  14.  
  15.     // str = 'unused: ' + str; //刪除該條語句 
  16.  
  17.   } 
  18.  
  19.   function getData() { 
  20.  
  21.     return 'data'
  22.  
  23.   } 
  24.  
  25.   return getData; 
  26.  
  27.  
  28. var list = [];     
  29.  
  30. document.querySelector('#click_button').addEventListener('click'function () { 
  31.  
  32.   list.push(f()); 
  33.  
  34. }, false); 

getData()和unused()內部函數共享f函數對應的變量對象,因為unused()內部函數訪問了f作用域內str變量,所以str字段存在于f變量對象中。加上getData()內部函數被返回,被其他對象引用,形成了閉包,因此對應的f變量對象存在于閉包函數的作用域鏈中。這里只要將函數unused中str = 'unused: ' + str;語句刪除便可解決問題。

查看一下閉包信息:

DOM泄露

在JavaScript中,DOM操作是非常耗時的。因為JavaScript/ECMAScript引擎獨立于渲染引擎,而DOM是位于渲染引擎,相互訪問需要消耗一定的資源。如Chrome瀏覽器中DOM位于WebCore,而JavaScript/ECMAScript位于V8中。假如將JavaScript/ECMAScript、DOM分別想象成兩座孤島,兩島之間通過一座收費橋連接,過橋需要交納一定“過橋費”。JavaScript/ECMAScript每次訪問DOM時,都需要交納“過橋費”。因此訪問DOM次數越多,費用越高,頁面性能就會受到很大影響。了解更多ℹ️

[[212467]]

為了減少DOM訪問次數,一般情況下,當需要多次訪問同一個DOM方法或屬性時,會將DOM引用緩存到一個局部變量中。但如果在執行某些刪除、更新操作后,可能會忘記釋放掉代碼中對應的DOM引用,這樣會造成DOM內存泄露。

實例------>demos/dom.html

  1. <!DOCTYPE html> 
  2. <html lang="en"
  3. <head> 
  4.   <meta charset="UTF-8"
  5.   <meta name="viewport" content="width=device-width, initial-scale=1.0"
  6.   <meta http-equiv="X-UA-Compatible" content="ie=edge"
  7.   <title>Dom-Leakage</title> 
  8. </head> 
  9. <body> 
  10.   <input type="button" value="add" class="add"
  11.   <input type="button" value="remove" class="remove" style="display:none;"
  12.  
  13.   <div class="container"
  14.     <pre class="wrapper"></pre> 
  15.   </div> 
  16.   <script> 
  17.     // 因為要多次用到pre.wrapper、div.container、input.remove、input.add節點,將其緩存到本地變量中 
  18.     var wrapper = document.querySelector('.wrapper'); 
  19.     var container = document.querySelector('.container'); 
  20.     var removeBtn = document.querySelector('.remove'); 
  21.     var addBtn = document.querySelector('.add'); 
  22.     var counter = 0; 
  23.     var once = true
  24.     // 方法 
  25.     var hide = function(target){ 
  26.       target.style.display = 'none'
  27.     } 
  28.     var show = function(target){ 
  29.       target.style.display = 'inline-block'
  30.     } 
  31.     // 回調函數 
  32.     var removeCallback = function(){ 
  33.       removeBtn.removeEventListener('click', removeCallback, false); 
  34.       addBtn.removeEventListener('click', addCallback, false); 
  35.       hide(addBtn); 
  36.       hide(removeBtn); 
  37.       container.removeChild(wrapper); 
  38.     } 
  39.     var addCallback = function(){ 
  40.       wrapper.appendChild(document.createTextNode('\t' + ++counter + ':a new line text\n')); 
  41.       // 顯示刪除操作按鈕 
  42.       if(once){ 
  43.         show(removeBtn); 
  44.         once = false
  45.       } 
  46.     } 
  47.     // 綁定事件 
  48.     removeBtn.addEventListener('click', removeCallback, false); 
  49.     addBtn.addEventListener('click', addCallback, false); 
  50.   </script> 
  51. </body> 
  52. </html> 

這里結合Chrome瀏覽器的Devtools–>Performance做一些分析,操作步驟如下:

⚠️注:***在隱藏窗口中進行分析工作,避免瀏覽器插件影響分析結果

  1. 開啟【Performance】項的記錄
  2. 執行一次CG,創建基準參考線
  3. 連續單擊【add】按鈕6次,增加6個文本節點到pre元素中
  4. 單擊【remove】按鈕,刪除剛增加6個文本節點和pre元元素
  5. 執行一次CG
  6. 停止記錄堆分析

從分析結果圖可知,雖然6次add操作增加6個Node,但是remove操作并沒有讓Nodes節點數下降,即remove操作失敗。盡管還主動執行了一次CG操作,Nodes曲線也沒有下降。因此可以斷定內存泄露了!那問題來了,如何去查找問題的原因呢?這里可以通過Chrome瀏覽器的Devtools–>Memory進行診斷分析,執行如下操作步驟:

⚠️注:***在隱藏窗口中進行分析工作,避免瀏覽器插件影響分析結果

  1. 選中【Take heap snapshot】選項
  2. 連續單擊【add】按鈕6次,增加6個文本節點到pre元素中
  3. 單擊【Take snapshot】按鈕,執行一次堆快照
  4. 單擊【remove】按鈕,刪除剛增加6個文本節點和pre元元素
  5. 單擊【Take snapshot】按鈕,執行一次堆快照
  6. 選中生成的第二個快照報告,并將視圖由"Summary"切換到"Comparison"對比模式,在[class filter]過濾輸入框中輸入關鍵字:Detached

從分析結果圖可知,導致整個pre元素和6個文本節點無法別回收的原因是:代碼中存在全局變量wrapper對pre元素的引用。知道了產生的問題原因,便可對癥下藥了。對代碼做如下就修改:

 

  1. // 因為要多次用到pre.wrapper、div.container、input.remove、input.add節點,將其緩存到本地變量中 
  2.  
  3. var wrapper = document.querySelector('.wrapper'); 
  4.  
  5. var container = document.querySelector('.container'); 
  6.  
  7. var removeBtn = document.querySelector('.remove'); 
  8.  
  9. var addBtn = document.querySelector('.add'); 
  10.  
  11. var counter = 0; 
  12.  
  13. var once = true
  14.  
  15. // 方法 
  16.  
  17. var hide = function(target){ 
  18.  
  19.   target.style.display = 'none'
  20.  
  21.  
  22. var show = function(target){ 
  23.  
  24.   target.style.display = 'inline-block'
  25.  
  26.  
  27. // 回調函數 
  28.  
  29. var removeCallback = function(){ 
  30.  
  31.   removeBtn.removeEventListener('click', removeCallback, false); 
  32.  
  33.   addBtn.removeEventListener('click', addCallback, false); 
  34.  
  35.   hide(addBtn); 
  36.  
  37.   hide(removeBtn); 
  38.  
  39.   container.removeChild(wrapper);  
  40.   
  41.  
  42.   wrapper = null;//在執行刪除操作時,將wrapper對pre節點的引用釋放掉 
  43.  
  44.  
  45. var addCallback = function(){ 
  46.  
  47.   wrapper.appendChild(document.createTextNode('\t' + ++counter + ':a new line text\n')); 
  48.  
  49.   // 顯示刪除操作按鈕 
  50.  
  51.   if(once){ 
  52.  
  53.     show(removeBtn); 
  54.  
  55.     once = false
  56.  
  57.   } 
  58.  
  59.  
  60. // 綁定事件 
  61.  
  62. removeBtn.addEventListener('click', removeCallback, false); 
  63.  
  64. addBtn.addEventListener('click', addCallback, false); 

在執行刪除操作時,將wrapper對pre節點的引用釋放掉,即在刪除邏輯中增加wrapper = null;語句。再次在Devtools–>Performance中重復上述操作:

小試牛刀------>demos/dom_practice.html

再來看看網上的一個實例,代碼如下:

 

  1. <!DOCTYPE html> 
  2.  
  3. <html lang="en"
  4.  
  5. <head> 
  6.  
  7.   <meta charset="UTF-8"
  8.  
  9.   <meta name="viewport" content="width=device-width, initial-scale=1.0"
  10.  
  11.   <meta http-equiv="X-UA-Compatible" content="ie=edge"
  12.  
  13.   <title>Practice</title> 
  14.  
  15. </head> 
  16.  
  17. <body> 
  18.  
  19.   <div id="refA"><ul><li><a href="#"></a></li><li><a href="#"></a></li><li><a href="#" id="refB"></a></li></ul></div> 
  20.  
  21.   <div></div> 
  22.  
  23.   <div></div> 
  24.  
  25.  
  26.  
  27.   <script> 
  28.  
  29.     var refA = document.getElementById('refA'); 
  30.  
  31.     var refB = document.getElementById('refB'); 
  32.  
  33.     document.body.removeChild(refA); 
  34.  
  35.  
  36.  
  37.     // #refA不能GC回收,因為存在變量refA對它的引用。將其對#refA引用釋放,但還是無法回收#refA。 
  38.  
  39.     refA = null
  40.  
  41.     // 還存在變量refB對#refA的間接引用(refB引用了#refB,而#refB屬于#refA)。將變量refB對#refB的引用釋放,#refA就可以被GC回收。 
  42.  
  43.     refB = null
  44.  
  45.   </script> 
  46.  
  47. </body> 
  48.  
  49. </html> 

整個過程如下圖所演示:

[[212468]]

有興趣的同學可以使用Chrome的Devtools工具,驗證一下分析結果,實踐很重要~~~high_brightness

timers

在JavaScript常用setInterval()來實現一些動畫效果。當然也可以使用鏈式setTimeout()調用模式來實現:

  1. setTimeout(function() { 
  2.   // do something. . . . 
  3.   setTimeout(arguments.callee, interval); 
  4. }, interval); 

如果在不需要setInterval()時,沒有通過clearInterval()方法移除,那么setInterval()會不停地調用函數,直到調用clearInterval()或窗口關閉。如果鏈式setTimeout()調用模式沒有給出終止邏輯,也會一直運行下去。因此再不需要重復定時器時,確保對定時器進行清除,避免占用系統資源。另外,在使用setInterval()和setTimeout()來實現動畫時,無法確保定時器按照指定的時間間隔來執行動畫。為了能在JavaScript中創建出平滑流暢的動畫,瀏覽器為JavaScript動畫添加了一個新API-requestAnimationFrame()。關于setInterval、setTimeout與requestAnimationFrame實現動畫上的區別➹猛擊😊

實例------>demos/timers.html

如下通過setInterval()實現一個clock的小實例,不過代碼存在問題的,有興趣的同學可以先嘗試找一下問題的所在~😎

操作:

  • 單擊【start】按鈕開始clock,同時web開發控制臺會打印實時信息
  • 單擊【stop】按鈕停止clock,同時web開發控制臺會輸出停止信息

 

  1. <!DOCTYPE html> 
  2.  
  3. <html lang="en"
  4.  
  5. <head> 
  6.  
  7.   <meta charset="UTF-8"
  8.  
  9.   <meta name="viewport" content="width=device-width, initial-scale=1.0"
  10.  
  11.   <meta http-equiv="X-UA-Compatible" content="ie=edge"
  12.  
  13.   <title>setInterval</title> 
  14.  
  15. </head> 
  16.  
  17. <body> 
  18.  
  19.   <input type="button" value="start" class="start"
  20.  
  21.   <input type="button" value="stop" class="stop">  
  22.  
  23.  
  24.   <script> 
  25.  
  26.     var counter = 0; 
  27.  
  28.     var clock = { 
  29.  
  30.       start: function () { 
  31.  
  32.         setInterval(this.step.bind(null, ++counter), 1000); 
  33.  
  34.       }, 
  35.  
  36.       step: function (flag) { 
  37.  
  38.         var date = new Date(); 
  39.  
  40.         var h = date.getHours(); 
  41.  
  42.         var m = date.getMinutes(); 
  43.  
  44.         var s = date.getSeconds(); 
  45.  
  46.         console.log("%d-----> %d:%d:%d", flag, h, m, s); 
  47.  
  48.       } 
  49.  
  50.     } 
  51.  
  52.     document.querySelector('.start').addEventListener('click', clock.start.bind(clock), false); 
  53.  
  54.     document.querySelector('.stop').addEventListener('click'function () { 
  55.  
  56.       console.log('----> stop <----'); 
  57.  
  58.       clock = null
  59.  
  60.     }, false); 
  61.  
  62.   </script> 
  63.  
  64. </body> 
  65.  
  66. </html> 

上述代碼存在兩個問題:

  1. 如果不斷的單擊【start】按鈕,會斷生成新的clock。
  2. 單擊【stop】按鈕不能停止clock。

輸出結果:

針對暴露出的問題,對代碼做如下修改:

 

  1. var counter = 0; 
  2.  
  3. var clock = { 
  4.  
  5.   timer: null
  6.  
  7.   start: function () { 
  8.  
  9.     // 解決***個問題 
  10.  
  11.     if (this.timer) { 
  12.  
  13.       clearInterval(this.timer); 
  14.  
  15.     } 
  16.  
  17.     this.timer = setInterval(this.step.bind(null, ++counter), 1000); 
  18.  
  19.   }, 
  20.  
  21.   step: function (flag) { 
  22.  
  23.     var date = new Date(); 
  24.  
  25.     var h = date.getHours(); 
  26.  
  27.     var m = date.getMinutes(); 
  28.  
  29.     var s = date.getSeconds(); 
  30.  
  31.     console.log("%d-----> %d:%d:%d", flag, h, m, s); 
  32.  
  33.   }, 
  34.  
  35.   // 解決第二個問題 
  36.  
  37.   destroy: function () { 
  38.  
  39.     console.log('----> stop <----'); 
  40.  
  41.     clearInterval(this.timer); 
  42.  
  43.     node = null
  44.  
  45.     counter = void(0); 
  46.  
  47.   } 
  48.  
  49.  
  50. document.querySelector('.start').addEventListener('click', clock.start.bind(clock), false); 
  51.  
  52. document.querySelector('.stop').addEventListener('click', clock.destroy.bind(clock), false); 

EventListener

做移動開發時,需要對不同設備尺寸做適配。如在開發組件時,有時需要考慮處理橫豎屏適配問題。一般做法,在橫豎屏發生變化時,需要將組件銷毀后再重新生成。而在組件中會對其進行相關事件綁定,如果在銷毀組件時,沒有將組件的事件解綁,在橫豎屏發生變化時,就會不斷地對組件進行事件綁定。這樣會導致一些異常,甚至可能會導致頁面崩掉。

實例------>demos/callbacks.html

 

  1. <!DOCTYPE html> 
  2.  
  3. <html lang="en"
  4.  
  5. <head> 
  6.  
  7.   <meta charset="UTF-8"
  8.  
  9.   <meta name="viewport" content="width=device-width, initial-scale=1.0"
  10.  
  11.   <meta http-equiv="X-UA-Compatible" content="ie=edge"
  12.  
  13.   <title>callbacks</title> 
  14.  
  15. </head> 
  16.  
  17. <body> 
  18.  
  19.   <div class="container"></div> 
  20.  
  21.   <script> 
  22.  
  23.     var container = document.querySelector('.container'); 
  24.  
  25.     var counter = 0; 
  26.  
  27.     var createHtml = function (n, counter) { 
  28.  
  29.       var template = `${(new Array(n)).join(`<div>${counter}: this is a new data <input type="button" value="remove"></div>`)}` 
  30.  
  31.       container.innerHTML = template; 
  32.  
  33.     } 
  34.  
  35.     
  36.  
  37.     var resizeCallback = function (init) { 
  38.  
  39.       createHtml(10, ++counter); 
  40.  
  41.       // 事件委托 
  42.  
  43.       container.addEventListener('click'function (event){ 
  44.  
  45.         var target = event.target; 
  46.  
  47.           if(target.tagName === 'INPUT'){ 
  48.  
  49.               container.removeChild(target.parentElement) 
  50.  
  51.           } 
  52.  
  53.       }, false);    
  54.  
  55.     } 
  56.  
  57.     window.addEventListener('resize', resizeCallback, false); 
  58.  
  59.     resizeCallback(true); 
  60.  
  61.   </script> 
  62.  
  63. </body> 
  64.  
  65. </html> 

頁面是存在問題的,這里結合Devtools–>Performance分析一下問題所在,操作步驟如下:

⚠️注:***在隱藏窗口中進行分析工作,避免瀏覽器插件影響分析結果

  1. 開啟Performance項的記錄
  2. 執行一次CG,創建基準參考線
  3. 對窗口大小進行調整
  4. 執行一次CG
  5. 停止記錄

如分析結果所示,在窗口大小變化時,會不斷地對container添加代理事件。

同一個元素節點注冊了多個相同的EventListener,那么重復的實例會被拋棄。這么做不會讓得EventListener被重復調用,也不需要用removeEventListener手動清除多余的EventListener,因為重復的都被自動拋棄了。而這條規則只是針對于命名函數。對于匿名函數,瀏覽器會將其看做不同的EventListener,所以只要將匿名的EventListener,命名一下就可以解決問題:

  1. var container = document.querySelector('.container'); 
  2.     var counter = 0; 
  3.     var createHtml = function (n, counter) { 
  4.       var template = `${(new Array(n)).join(`<div>${counter}: this is a new data <input type="button" value="remove"></div>`)}` 
  5.       container.innerHTML = template; 
  6.     } 
  7.     //  
  8.     var clickCallback = function (event) { 
  9.       var target = event.target; 
  10.       if (target.tagName === 'INPUT') { 
  11.         container.removeChild(target.parentElement) 
  12.       } 
  13.     } 
  14.     var resizeCallback = function (init) { 
  15.       createHtml(10, ++counter); 
  16.       // 事件委托 
  17.       container.addEventListener('click', clickCallback, false); 
  18.     } 
  19.     window.addEventListener('resize', resizeCallback, false); 
  20.     resizeCallback(true); 

在Devtools–>Performance中再重復上述操作,分析結果如下:

在開發中,開發者很少關注事件解綁,因為瀏覽器已經為我們處理得很好了。不過在使用第三方庫時,需要特別注意,因為一般第三方庫都實現了自己的事件綁定,如果在使用過程中,在需要銷毀事件綁定時,沒有調用所解綁方法,就可能造成事件綁定數量的不斷增加。如下鏈接是我在項目中使用jquery,遇見到類似問題:jQuery中忘記解綁注冊的事件,造成內存泄露➹猛擊😊

總結

本文主要介紹了幾種常見的內存泄露。在開發過程,需要我們特別留意一下本文所涉及到的幾種內存泄露問題。因為這些隨時可能發生在我們日常開發中,如果我們對它們不了解是很難發現它們的存在??赡茉谒鼈儗栴}影響程度放大時,才會引起我們的關注。不過那時可能就晚了,因為產品可能已經上線,接著就會嚴重影響產品的質量和用戶體驗,甚至可能讓我們承受大量用戶流失的損失。作為開發的我們必須把好這個關,讓我們開發的產品帶給用戶***的體驗。

參考文章:

責任編輯:龐桂玉 來源: segmentfault
相關推薦

2023-04-21 09:35:50

2019-05-28 10:54:14

Linux運行系統

2021-12-03 05:35:56

Windows 11操作系統微軟

2014-11-25 10:03:42

JavaScript

2012-12-17 13:51:22

Web前端JavaScriptJS

2009-06-24 15:00:39

Javascript代

2019-07-08 17:30:47

智能

2024-11-21 16:46:12

2020-11-05 10:05:49

螞蟻互聯網上市

2021-11-22 08:14:23

Linux Linux驅動Linux 系統

2018-06-11 15:30:12

2013-03-18 16:09:27

JavaEEOpenfire

2022-08-29 15:19:09

CSS煙花動畫

2009-07-06 19:29:37

云計算私有云服務器虛擬化

2016-11-02 18:54:01

javascript

2018-10-10 14:02:39

前端JavaScript函數

2019-08-26 18:20:05

JavascriptWeb前端

2017-03-15 17:41:16

華為生態

2017-03-07 15:54:13

華為
點贊
收藏

51CTO技術棧公眾號

国产91色在线免费| 精品日韩av一区二区| 欧美一区二区影视| 亚洲一卡二卡在线观看| 午夜精品影视国产一区在线麻豆| 精品久久在线播放| 明星裸体视频一区二区| 最好看的日本字幕mv视频大全| 欧美综合一区| 4hu四虎永久在线影院成人| 公共露出暴露狂另类av| 韩国av在线免费观看| 国产情侣一区| 亚洲天堂日韩电影| 在线观看中文av| 91九色porn在线资源| 91蜜桃视频在线| 国产精品日韩专区| 久久精品99久久久久久| 欧美美乳视频| 在线观看免费一区| 视频在线一区二区三区| 精品国产亚洲av麻豆| 国产视频亚洲| 久久精品国产清自在天天线| 亚洲啪av永久无码精品放毛片 | 日韩精品在线网站| 国内外成人激情视频| 最近高清中文在线字幕在线观看| 国产精品亚洲视频| 国产精品久久久久久一区二区| 亚洲 欧美 变态 另类 综合| 亚洲精品国产动漫| 91精品国产一区二区三区蜜臀| 全黄性性激高免费视频| freemovies性欧美| 成人h版在线观看| 国产日韩欧美另类| www.毛片.com| 国产精品99免费看| 日韩性生活视频| 国产熟妇搡bbbb搡bbbb| 亚洲成av人片在线观看www| 天天影视色香欲综合网老头| 四虎免费在线观看视频| 国产在线观看免费| av在线播放不卡| 亚洲一区二区三区乱码aⅴ| 欧美一区二区三区网站| 激情婷婷欧美| 操人视频在线观看欧美| 精品无码人妻一区二区免费蜜桃| 狠狠久久伊人| 日韩一区二区免费视频| 99re6在线观看| 精品国产免费人成网站| 亚洲成av人综合在线观看| 在线码字幕一区| 中文字幕在线观看日本| 久久综合久色欧美综合狠狠| 国产精品久久7| 国产成人麻豆精品午夜在线| 精品制服美女久久| 国产精品香蕉在线观看| 黄色在线免费观看| 亚洲女人av| 性欧美xxxx| 国产精久久久久久| 狠狠色综合网| 欧美精品福利在线| 国产真实乱偷精品视频| 午夜电影亚洲| 欧美另类69精品久久久久9999| 青青操在线视频观看| 青青草国产免费一区二区下载| 亚洲精品一区二区三区香蕉| 日批视频免费看| 亚洲精品观看| 亚洲电影免费观看高清完整版在线| av在线免费观看不卡| 欧美激情精品| 欧美变态tickling挠脚心| 国产免费a级片| 国产视频一区二| 日韩视频123| 女同性αv亚洲女同志| 国产精品毛片视频| 亚洲国产日韩欧美在线图片| 精品视频站长推荐| 欧美黑白配在线| 亚洲午夜国产成人av电影男同| 国产精品无码电影| 国产成人一区二区三区影院| 亚洲午夜av久久乱码| 日本黄区免费视频观看| 图片区亚洲欧美小说区| 欧美风情在线观看| 日韩黄色精品视频| 爽好多水快深点欧美视频| 国产精品国语对白| 国产精品久久久久久免费免熟 | 91在线视频| 亚洲日本va午夜在线影院| 嫩草影院中文字幕| 五月天国产在线| 欧美视频在线视频| 奇米影视四色在线| 国产suv精品一区二区四区视频| 日韩av在线一区| 婷婷色一区二区三区| 羞羞色午夜精品一区二区三区| 欧美激情一区二区三区高清视频| 在线观看免费国产视频| 久久久久99| 国产有码在线一区二区视频| 亚洲成人77777| 久久久电影一区二区三区| 伊人色综合影院| 麻豆免费在线| 欧美精品tushy高清| 亚洲中文字幕一区| 久久国产电影| 91国内揄拍国内精品对白| 亚洲天堂aaa| 99热在这里有精品免费| 一区二区三区欧美成人| 在线看片福利| 日韩视频免费观看高清完整版在线观看 | 最新国产精品自拍| 色135综合网| 91chinesevideo永久地址| 午夜一区二区三区四区| 成人av高清在线| 神马午夜伦理影院| 欧美色网在线| 亚洲精品国产精品国自产观看浪潮| 亚洲欧美va天堂人熟伦| 日韩午夜在线电影| 99re国产视频| 男人天堂手机在线| 欧美天堂在线观看| 在线观看免费视频国产| 天天射天天综合网| 国产精品久久久久久久一区探花| 免费成人在线看| 亚洲人吸女人奶水| 91制片厂毛片| 亚洲黄色录像| 91精品国产高清久久久久久| 亚洲精品一区二区三区四区| 亚洲摸摸操操av| 亚洲综合欧美在线| 操欧美老女人| 国产精品白嫩初高中害羞小美女 | 高清国产一区| 中文字幕免费高清电视剧网站在线观看 | 91精品国产综合久久久久久久久久| 国产一级久久久久毛片精品| 亚洲福利免费| 国产精品裸体一区二区三区| 天堂va在线| 精品久久一区二区| 久久国产露脸精品国产| 国产精品亚洲综合一区在线观看| 先锋在线资源一区二区三区| 制服诱惑亚洲| 国产亚洲精品久久久优势 | 色网综合在线观看| 精品无码在线视频| 午夜在线一区二区| 欧美精品成人一区二区在线观看 | 中文字幕资源网| 中文字幕高清一区| 色噜噜狠狠永久免费| 久久99国产成人小视频| 国产成人精品日本亚洲专区61| 手机亚洲第一页| 欧美日韩在线影院| 久久精品国产亚洲AV熟女| 久久久久一区| 日韩av一级大片| 欧美美女被草| 欧美床上激情在线观看| 好吊色一区二区| 狠狠色狠色综合曰曰| 国产人妻大战黑人20p| 美腿丝袜亚洲三区| 天堂а√在线中文在线| 精品中国亚洲| 国产精品88a∨| 免费网站成人| 日韩午夜电影av| 日韩精品一区二区av| 国产亚洲污的网站| 肉色超薄丝袜脚交| 影音先锋中文字幕一区| 欧美亚州在线观看| 成人51免费| 午夜精品国产精品大乳美女| 成年人视频在线看| 日韩一区二区三区视频| 在线视频一区二区三区四区| 久久久一区二区| 中文字幕精品一区二区三区在线| 亚洲有吗中文字幕| 久久人人爽爽人人爽人人片av| 91亚洲精品| 欧美成人精品一区二区| 日本精品专区| 91精品国产综合久久精品| 亚洲免费在线观看av| 国产精品网站导航| 国产乱淫av片| 美女视频网站久久| 日本a视频在线观看| 热久久天天拍国产| 国产综合第一页| 亚洲男人在线| 欧美精品成人91久久久久久久| 国产精品无码2021在线观看| 日韩欧美在线影院| 一二三区免费视频| 亚洲午夜av在线| 777777国产7777777| 91女人视频在线观看| 日韩av福利在线观看| 天堂成人国产精品一区| 精品人妻人人做人人爽| 成人在线免费观看视频| 国语精品中文字幕| 国产一区二区三区免费在线| 国产精品三级美女白浆呻吟 | 国产精品女人毛片| 欧美高清性xxxx| 国产自产视频一区二区三区| 六月丁香婷婷在线| 在线看片一区| 欧美黄色免费网址| 亚洲国产一区二区三区在线播放| 欧美日韩国产综合在线| silk一区二区三区精品视频| 91精品久久久久久久久| segui88久久综合9999| 欧美情侣性视频| 国产美女av在线| 日韩视频第一页| 在线免费观看黄色网址| 亚洲国产精品小视频| 午夜美女福利视频| 91精品国产综合久久福利| 国产精品久久久久久久久久久久久久久久久久 | 蜜臀久久99精品久久久无需会员| 蜜桃成人在线视频| 日韩激情视频在线| 亚洲人妻一区二区| 国产偷亚洲偷欧美偷精品| 高清毛片aaaaaaaaa片| 日韩一区二区麻豆国产| 99热这里只有精品9| 欧美少妇性性性| 成人黄色片在线观看 | 一区二区三区亚洲视频| 欧美日韩国产不卡| 在线观看毛片网站| 欧美日韩一区二区三区四区| 亚洲毛片一区二区三区| 91久久精品网| 久久这里只有精品9| 欧美少妇一区二区| 97超碰人人模人人人爽人人爱| 欧美老女人在线| 99热这里只有精品在线| 日韩av在线网| 日本在线免费| 久久99久久99精品中文字幕| av漫画网站在线观看| 国产福利精品在线| 欧美.com| 日本高清久久一区二区三区| 99欧美视频| 成人一对一视频| 六月丁香综合在线视频| 88av在线播放| 最新成人av在线| 日韩av片在线播放| 欧美日韩精品一区二区三区蜜桃 | 久久久国产一区| 狼人综合视频| 96pao国产成视频永久免费| 美女呻吟一区| 久久久无码中文字幕久...| 免费日韩视频| 精品国产午夜福利在线观看| 久久久久久免费| 精品一级少妇久久久久久久| 欧洲色大大久久| 天堂av一区二区三区| 深夜福利一区二区| 蜜桃麻豆影像在线观看| 亚洲a中文字幕| 精品不卡一区| 三上悠亚久久精品| 韩国av一区二区三区| 国产精品亚洲无码| 亚洲午夜影视影院在线观看| 中文字幕一区二区人妻痴汉电车| 亚洲高清免费观看高清完整版| jyzzz在线观看视频| 97免费视频在线| 久久丁香四色| 一区二区三区在线视频111| 国产欧美三级| 亚洲熟女一区二区三区| **欧美大码日韩| 夜夜嗨aⅴ一区二区三区| 国产视频精品在线| 激情图片在线观看高清国产| 91精品国产综合久久男男| 国产欧美日韩精品一区二区三区| 日韩视频免费播放| 国产乱子伦视频一区二区三区| 三级男人添奶爽爽爽视频| 亚洲久草在线视频| 91国内精品视频| 一区二区欧美日韩视频| 日韩av大片站长工具| 久久综合九色欧美狠狠| 亚洲无毛电影| 午夜影院福利社| 亚洲精品成人精品456| 国产精品探花视频| www国产精品视频| 国产一区二区精品调教| 日韩理论片在线观看| 久久久噜噜噜| 丰满少妇高潮一区二区| 日本韩国欧美一区| 国产在线视频网| 国产成人久久久精品一区| 免费欧美激情| 男操女免费网站| 国产精品乱码一区二区三区软件| 中文在线免费观看| 色偷偷av一区二区三区乱| 久久久久久一区二区三区四区别墅| 日韩欧美一区二区三区四区| 日韩av一级电影| 美国黄色特级片| 欧美日韩在线一区二区| 在线免费观看的av网站| 91久久精品一区| 欧美久久一级| 亚洲一区二区三区黄色| 舔着乳尖日韩一区| 欧美日韩国产亚洲沙发| 国产福利视频一区二区| 四季av一区二区凹凸精品| www.五月天色| 亚洲一区二区3| 亚洲人在线观看视频| 国产精品劲爆视频| 国产精品二区不卡| 男人女人拔萝卜视频| 亚洲mv在线观看| 黄色国产在线| 91精品在线影院| 亚洲国产一区二区三区a毛片 | 天天操精品视频| 香蕉影视欧美成人| 国产三级电影在线观看| 成人福利视频在线观看| 欧美视频导航| 黄色aaa视频| 欧美一区二区三区爱爱| 精精国产xxx在线视频app| 日韩中文字幕一区二区| 国产一区二区在线影院| 国产精品第一页在线观看| 亚洲人成人99网站| 国产一区二区三区| 国产又大又硬又粗| 国产精品久久久久久久久动漫 | xnxx国产精品| 一级黄色免费看| 97精品在线视频| 色天天综合网| 日韩aaaaa| 欧美军同video69gay| 涩涩网在线视频| 日韩中文在线字幕| 久久精品夜夜夜夜久久| a毛片在线免费观看| 日本精品久久电影| 欧美国产另类| 国产精品免费无码| 精品88久久久久88久久久| yy6080久久伦理一区二区| 黄色大片中文字幕| 亚洲女女做受ⅹxx高潮| 成人在线免费电影|