(關注福利,關注本公衆號回覆[資料]領取優質前端視頻,包括Vue、React、Node源碼和實戰、面試指導)前端
本週正式開始前端進階的第一期,本週的主題是調用堆棧,今天是第5天。node
本計劃一共28期,每期重點攻克一個面試重難點,若是你還不瞭解本進階計劃,點擊查看前端進階的破冰之旅webpack
若是以爲本系列不錯,歡迎轉發,您的支持就是我堅持的最大動力。git
4類 JavaScript 內存泄漏及如何避免 ,因爲微信不能訪問外鏈,點擊閱讀原文就能夠啦。es6
上篇文章介紹了垃圾回收機制,可是都是些概念,今日份文章(譯文)有代碼有講解,詳解介紹了經常使用內存泄漏並說明了如何避免,對於提高我的知識深度很是有幫助。github
上篇文章詳細介紹了內存回收和內存泄漏,今天咱們繼續這個篇幅,不太重點是內存泄漏可能發生的緣由。沒看過上篇的點擊【進階1-4期】JavaScript深刻之帶你走進內存機制web
經常使用垃圾回收算法叫作標記清除 (Mark-and-sweep) ,算法由如下幾步組成:面試
現代的垃圾回收器改良了算法,可是本質是相同的:可達內存被標記,其他的被看成垃圾回收。算法
劃重點 這是個考點跨域
未定義的變量會在全局對象建立一個新變量,以下。
function foo(arg) { bar = "this is a hidden global variable"; }
函數 foo
內部忘記使用 var
,實際上JS會把bar掛載到全局對象上,意外建立一個全局變量。
function foo(arg) { window.bar = "this is an explicit global variable"; }
另外一個意外的全局變量可能由 this
建立。
function foo() { this.variable = "potential accidental global"; } // Foo 調用本身,this 指向了全局對象(window) // 而不是 undefined foo();
解決方法:
在 JavaScript 文件頭部加上 'use strict'
,使用嚴格模式避免意外的全局變量,此時上例中的this指向undefined
。若是必須使用全局變量存儲大量數據時,確保用完之後把它設置爲 null 或者從新定義。
計時器setInterval
代碼很常見
var someResource = getData(); setInterval(function() { var node = document.getElementById('Node'); if(node) { // 處理 node 和 someResource node.innerHTML = JSON.stringify(someResource)); } }, 1000);
上面的例子代表,在節點node或者數據再也不須要時,定時器依舊指向這些數據。因此哪怕當node節點被移除後,interval 仍舊存活而且垃圾回收器沒辦法回收,它的依賴也沒辦法被回收,除非終止定時器。
var element = document.getElementById('button'); function onClick(event) { element.innerHTML = 'text'; } element.addEventListener('click', onClick);
對於上面觀察者的例子,一旦它們再也不須要(或者關聯的對象變成不可達),明確地移除它們很是重要。老的 IE 6 是沒法處理循環引用的。由於老版本的 IE 是沒法檢測 DOM 節點與 JavaScript 代碼之間的循環引用,會致使內存泄漏。
可是,現代的瀏覽器(包括 IE 和 Microsoft Edge)使用了更先進的垃圾回收算法(標記清除),已經能夠正確檢測和處理循環引用了。即回收節點內存時,沒必要非要調用 removeEventListener
了。
若是把DOM 存成字典(JSON 鍵值對)或者數組,此時,一樣的 DOM 元素存在兩個引用:一個在 DOM 樹中,另外一個在字典中。那麼未來須要把兩個引用都清除。
var elements = { button: document.getElementById('button'), image: document.getElementById('image'), text: document.getElementById('text') }; function doStuff() { image.src = 'http://some.url/image'; button.click(); console.log(text.innerHTML); // 更多邏輯 } function removeButton() { // 按鈕是 body 的後代元素 document.body.removeChild(document.getElementById('button')); // 此時,仍舊存在一個全局的 #button 的引用 // elements 字典。button 元素仍舊在內存中,不能被 GC 回收。 }
若是代碼中保存了表格某一個 <td>
的引用。未來決定刪除整個表格的時候,直覺認爲 GC 會回收除了已保存的 <td>
之外的其它節點。實際狀況並不是如此:此 <td>
是表格的子節點,子元素與父元素是引用關係。因爲代碼保留了 <td>
的引用,致使整個表格仍待在內存中。因此保存 DOM 元素引用的時候,要當心謹慎。
閉包的關鍵是匿名函數能夠訪問父級做用域的變量。
var theThing = null; var replaceThing = function () { var originalThing = theThing; var unused = function () { if (originalThing) console.log("hi"); }; theThing = { longStr: new Array(1000000).join('*'), someMethod: function () { console.log(someMessage); } }; }; setInterval(replaceThing, 1000);
每次調用 replaceThing
,theThing
獲得一個包含一個大數組和一個新閉包(someMethod
)的新對象。同時,變量 unused
是一個引用 originalThing
的閉包(先前的 replaceThing
又調用了 theThing
)。someMethod
能夠經過 theThing
使用,someMethod
與 unused
分享閉包做用域,儘管 unused
從未使用,它引用的 originalThing
迫使它保留在內存中(防止被回收)。
解決方法:
在 replaceThing
的最後添加 originalThing = null
。
PS:今晚弄到很晚,因爲時間問題,就再也不詳細介紹Chrome 內存剖析工具,有興趣的你們去原文查看。
週末彙總將在週日早上發送,週六會發送其餘類型的文章,敬請期待。
問題一:
從內存來看 null 和 undefined 本質的區別是什麼?
解答:
給一個全局變量賦值爲null,至關於將這個變量的指針對象以及值清空,若是是給對象的屬性 賦值爲null,或者局部變量賦值爲null,至關於給這個屬性分配了一塊空的內存,而後值爲null, JS會回收全局變量爲null的對象。
給一個全局變量賦值爲undefined,至關於將這個對象的值清空,可是這個對象依舊存在,若是是給對象的屬性賦值 爲undefined,說明這個值爲空值
擴展下:
聲明瞭一個變量,但未對其初始化時,這個變量的值就是undefined,它是 JavaScript 基本類型 之一。
var data; console.log(data === undefined); //true
對於還沒有聲明過的變量,只能執行一項操做,即便用typeof操做符檢測其數據類型,使用其餘的操做都會報錯。
//data變量未定義 console.log(typeof data); // "undefined" console.log(data === undefined); //報錯
值 null
特指對象的值未設置,它是 JavaScript 基本類型 之一。
值 null
是一個字面量,它不像undefined
是全局對象的一個屬性。null
是表示缺乏的標識,指示變量未指向任何對象。
// foo不存在,它歷來沒有被定義過或者是初始化過: foo; "ReferenceError: foo is not defined" // foo如今已是知存在的,可是它沒有類型或者是值: var foo = null; console.log(foo); // null
問題二:
ES6語法中的 const 聲明一個只讀的常量,那爲何下面能夠修改const的值?
const foo = {}; // 爲 foo 添加一個屬性,能夠成功 foo.prop = 123; foo.prop // 123 // 將 foo 指向另外一個對象,就會報錯 foo = {}; // TypeError: "foo" is read-only
解答:
const
實際上保證的,並非變量的值不得改動,而是變量指向的那個內存地址所保存的數據不得改動。對於簡單類型的數據(數值、字符串、布爾值),值就保存在變量指向的那個內存地址,所以等同於常量。但對於複合類型的數據(主要是對象和數組),變量指向的內存地址,保存的只是一個指向實際數據的指針,const
只能保證這個指針是固定的(即老是指向另外一個固定的地址),至於它指向的數據結構是否是可變的,就徹底不能控制了。所以,將一個對象聲明爲常量必須很是當心。
<script> console.log(fun) console.log(person) </script> <script> console.log(person) console.log(fun) var person = "Eric"; console.log(person) function fun() { console.log(person) var person = "Tom"; console.log(person) } fun() console.log(person) </script>
上面代碼的執行結果是什麼?先本身分析,而後再到瀏覽器中執行。
4類 JavaScript 內存泄漏及如何避免
每週面試重難點計劃以下,若有修改會通知你們。每週一期,爲期半年,準備明年跳槽的小夥伴們能夠把本公衆號[置頂]()了。
本人Github連接以下,歡迎各位Star
http://github.com/yygmind/blog
我是木易楊,網易高級前端工程師,跟着我每週重點攻克一個前端面試重難點。接下來讓我帶你走進高級前端的世界,在進階的路上,共勉!
若是你想加羣討論每期面試知識點,公衆號回覆[加羣]便可