摘要:本期推薦文章類內(nèi)存泄漏及如何避免,由于微信不能訪問外鏈,點擊閱讀原文就可以啦。四種常見的內(nèi)存泄漏劃重點這是個考點意外的全局變量未定義的變量會在全局對象創(chuàng)建一個新變量,如下。因為老版本的是無法檢測節(jié)點與代碼之間的循環(huán)引用,會導(dǎo)致內(nèi)存泄漏。
(關(guān)注福利,關(guān)注本公眾號回復(fù)[資料]領(lǐng)取優(yōu)質(zhì)前端視頻,包括Vue、React、Node源碼和實戰(zhàn)、面試指導(dǎo))
本周正式開始前端進(jìn)階的第一期,本周的主題是調(diào)用堆棧,今天是第5天。
本計劃一共28期,每期重點攻克一個面試重難點,如果你還不了解本進(jìn)階計劃,點擊查看前端進(jìn)階的破冰之旅
如果覺得本系列不錯,歡迎轉(zhuǎn)發(fā),您的支持就是我堅持的最大動力。
本期推薦文章4類 JavaScript 內(nèi)存泄漏及如何避免 ,由于微信不能訪問外鏈,點擊閱讀原文就可以啦。
推薦理由上篇文章介紹了垃圾回收機(jī)制,但是都是些概念,今日份文章(譯文)有代碼有講解,詳解介紹了常用內(nèi)存泄漏并說明了如何避免,對于提升個人知識深度非常有幫助。
閱讀筆記上篇文章詳細(xì)介紹了內(nèi)存回收和內(nèi)存泄漏,今天我們繼續(xù)這個篇幅,不過重點是內(nèi)存泄漏可能發(fā)生的原因。沒看過上篇的點擊【進(jìn)階1-4期】JavaScript深入之帶你走進(jìn)內(nèi)存機(jī)制
垃圾回收算法常用垃圾回收算法叫做標(biāo)記清除 (Mark-and-sweep) ,算法由以下幾步組成:
1、垃圾回收器創(chuàng)建了一個“roots”列表。roots 通常是代碼中全局變量的引用。JavaScript 中,“window” 對象是一個全局變量,被當(dāng)作 root 。window 對象總是存在,因此垃圾回收器可以檢查它和它的所有子對象是否存在(即不是垃圾);
2、所有的 roots 被檢查和標(biāo)記為激活(即不是垃圾)。所有的子對象也被遞歸地檢查。從 root 開始的所有對象如果是可達(dá)的,它就不被當(dāng)作垃圾。
3、所有未被標(biāo)記的內(nèi)存會被當(dāng)做垃圾,收集器現(xiàn)在可以釋放內(nèi)存,歸還給操作系統(tǒng)了。
現(xiàn)代的垃圾回收器改良了算法,但是本質(zhì)是相同的:可達(dá)內(nèi)存被標(biāo)記,其余的被當(dāng)作垃圾回收。
四種常見的JS內(nèi)存泄漏劃重點 這是個考點
未定義的變量會在全局對象創(chuàng)建一個新變量,如下。
function foo(arg) { bar = "this is a hidden global variable"; }
函數(shù) foo 內(nèi)部忘記使用 var ,實際上JS會把bar掛載到全局對象上,意外創(chuàng)建一個全局變量。
function foo(arg) { window.bar = "this is an explicit global variable"; }
另一個意外的全局變量可能由 this 創(chuàng)建。
function foo() { this.variable = "potential accidental global"; } // Foo 調(diào)用自己,this 指向了全局對象(window) // 而不是 undefined foo();
解決方法:
在 JavaScript 文件頭部加上 "use strict",使用嚴(yán)格模式避免意外的全局變量,此時上例中的this指向undefined。如果必須使用全局變量存儲大量數(shù)據(jù)時,確保用完以后把它設(shè)置為 null 或者重新定義。
計時器setInterval代碼很常見
var someResource = getData(); setInterval(function() { var node = document.getElementById("Node"); if(node) { // 處理 node 和 someResource node.innerHTML = JSON.stringify(someResource)); } }, 1000);
上面的例子表明,在節(jié)點node或者數(shù)據(jù)不再需要時,定時器依舊指向這些數(shù)據(jù)。所以哪怕當(dāng)node節(jié)點被移除后,interval 仍舊存活并且垃圾回收器沒辦法回收,它的依賴也沒辦法被回收,除非終止定時器。
var element = document.getElementById("button"); function onClick(event) { element.innerHTML = "text"; } element.addEventListener("click", onClick);
對于上面觀察者的例子,一旦它們不再需要(或者關(guān)聯(lián)的對象變成不可達(dá)),明確地移除它們非常重要。老的 IE 6 是無法處理循環(huán)引用的。因為老版本的 IE 是無法檢測 DOM 節(jié)點與 JavaScript 代碼之間的循環(huán)引用,會導(dǎo)致內(nèi)存泄漏。
但是,現(xiàn)代的瀏覽器(包括 IE 和 Microsoft Edge)使用了更先進(jìn)的垃圾回收算法(標(biāo)記清除),已經(jīng)可以正確檢測和處理循環(huán)引用了。即回收節(jié)點內(nèi)存時,不必非要調(diào)用 removeEventListener 了。
如果把DOM 存成字典(JSON 鍵值對)或者數(shù)組,此時,同樣的 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 元素仍舊在內(nèi)存中,不能被 GC 回收。 }
如果代碼中保存了表格某一個
閉包的關(guān)鍵是匿名函數(shù)可以訪問父級作用域的變量。
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);
每次調(diào)用 replaceThing ,theThing 得到一個包含一個大數(shù)組和一個新閉包(someMethod)的新對象。同時,變量 unused 是一個引用 originalThing 的閉包(先前的 replaceThing 又調(diào)用了 theThing )。someMethod 可以通過 theThing 使用,someMethod 與 unused 分享閉包作用域,盡管 unused 從未使用,它引用的 originalThing 迫使它保留在內(nèi)存中(防止被回收)。
解決方法:
在 replaceThing 的最后添加 originalThing = null 。
PS:今晚弄到很晚,由于時間問題,就不再詳細(xì)介紹Chrome 內(nèi)存剖析工具,有興趣的大家去原文查看。
周末匯總將在周日早上發(fā)送,周六會發(fā)送其他類型的文章,敬請期待。
昨日思考題解答問題一:
從內(nèi)存來看 null 和 undefined 本質(zhì)的區(qū)別是什么?
解答:
給一個全局變量賦值為null,相當(dāng)于將這個變量的指針對象以及值清空,如果是給對象的屬性 賦值為null,或者局部變量賦值為null,相當(dāng)于給這個屬性分配了一塊空的內(nèi)存,然后值為null, JS會回收全局變量為null的對象。
給一個全局變量賦值為undefined,相當(dāng)于將這個對象的值清空,但是這個對象依舊存在,如果是給對象的屬性賦值 為undefined,說明這個值為空值
擴(kuò)展下:
聲明了一個變量,但未對其初始化時,這個變量的值就是undefined,它是 JavaScript 基本類型 之一。
var data; console.log(data === undefined); //true
對于尚未聲明過的變量,只能執(zhí)行一項操作,即使用typeof操作符檢測其數(shù)據(jù)類型,使用其他的操作都會報錯。
//data變量未定義 console.log(typeof data); // "undefined" console.log(data === undefined); //報錯
值 null 特指對象的值未設(shè)置,它是 JavaScript 基本類型 之一。
值 null 是一個字面量,它不像undefined 是全局對象的一個屬性。null 是表示缺少的標(biāo)識,指示變量未指向任何對象。
// foo不存在,它從來沒有被定義過或者是初始化過: foo; "ReferenceError: foo is not defined" // foo現(xiàn)在已經(jīng)是知存在的,但是它沒有類型或者是值: 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實際上保證的,并不是變量的值不得改動,而是變量指向的那個內(nèi)存地址所保存的數(shù)據(jù)不得改動。對于簡單類型的數(shù)據(jù)(數(shù)值、字符串、布爾值),值就保存在變量指向的那個內(nèi)存地址,因此等同于常量。但對于復(fù)合類型的數(shù)據(jù)(主要是對象和數(shù)組),變量指向的內(nèi)存地址,保存的只是一個指向?qū)嶋H數(shù)據(jù)的指針,const只能保證這個指針是固定的(即總是指向另一個固定的地址),至于它指向的數(shù)據(jù)結(jié)構(gòu)是不是可變的,就完全不能控制了。因此,將一個對象聲明為常量必須非常小心。
今日思考題上面代碼的執(zhí)行結(jié)果是什么?先自己分析,然后再到瀏覽器中執(zhí)行。
參考4類 JavaScript 內(nèi)存泄漏及如何避免往期文章查看ECMAScript 6 入門之const 命令
【進(jìn)階1-1期】理解JavaScript 中的執(zhí)行上下文和執(zhí)行棧
【進(jìn)階1-2期】JavaScript深入之執(zhí)行上下文棧和變量對象
【進(jìn)階1-3期】JavaScript深入之內(nèi)存空間詳細(xì)圖解
【進(jìn)階1-4期】JavaScript深入之帶你走進(jìn)內(nèi)存機(jī)制制
【進(jìn)階1-5期】JavaScript深入之4類常見內(nèi)存泄漏及如何避免
【進(jìn)階2-1期】深入淺出圖解作用域鏈和閉包
每周計劃安排每周面試重難點計劃如下,如有修改會通知大家。每周一期,為期半年,準(zhǔn)備明年跳槽的小伙伴們可以把本公眾號[置頂]()了。
【進(jìn)階1期】 調(diào)用堆棧
【進(jìn)階2期】 作用域閉包
【進(jìn)階3期】 this全面解析
【進(jìn)階4期】 深淺拷貝原理
【進(jìn)階5期】 原型Prototype
【進(jìn)階6期】 高階函數(shù)
【進(jìn)階7期】 事件機(jī)制
【進(jìn)階8期】 Event Loop原理
【進(jìn)階9期】 Promise原理
【進(jìn)階10期】Async/Await原理
【進(jìn)階11期】防抖/節(jié)流原理
【進(jìn)階12期】模塊化詳解
【進(jìn)階13期】ES6重難點
【進(jìn)階14期】計算機(jī)網(wǎng)絡(luò)概述
【進(jìn)階15期】瀏覽器渲染原理
【進(jìn)階16期】webpack配置
【進(jìn)階17期】webpack原理
【進(jìn)階18期】前端監(jiān)控
【進(jìn)階19期】跨域和安全
【進(jìn)階20期】性能優(yōu)化
【進(jìn)階21期】VirtualDom原理
【進(jìn)階22期】Diff算法
【進(jìn)階23期】MVVM雙向綁定
【進(jìn)階24期】Vuex原理
【進(jìn)階25期】Redux原理
【進(jìn)階26期】路由原理
【進(jìn)階27期】VueRouter源碼解析
【進(jìn)階28期】ReactRouter源碼解析
交流本人Github鏈接如下,歡迎各位Star
http://github.com/yygmind/blog
我是木易楊,網(wǎng)易高級前端工程師,跟著我每周重點攻克一個前端面試重難點。接下來讓我?guī)阕哌M(jìn)高級前端的世界,在進(jìn)階的路上,共勉!
如果你想加群討論每期面試知識點,公眾號回復(fù)[加群]即可
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/99553.html
摘要:引擎對堆內(nèi)存中的對象進(jìn)行分代管理新生代存活周期較短的對象,如臨時變量字符串等。內(nèi)存泄漏對于持續(xù)運行的服務(wù)進(jìn)程,必須及時釋放不再用到的內(nèi)存。 (關(guān)注福利,關(guān)注本公眾號回復(fù)[資料]領(lǐng)取優(yōu)質(zhì)前端視頻,包括Vue、React、Node源碼和實戰(zhàn)、面試指導(dǎo)) 本周正式開始前端進(jìn)階的第一期,本周的主題是調(diào)用堆棧,今天是第4天。 本計劃一共28期,每期重點攻克一個面試重難點,如果你還不了解本進(jìn)階計劃...
摘要:進(jìn)階期理解中的執(zhí)行上下文和執(zhí)行棧進(jìn)階期深入之執(zhí)行上下文棧和變量對象但是今天補(bǔ)充一個知識點某些情況下,調(diào)用堆棧中函數(shù)調(diào)用的數(shù)量超出了調(diào)用堆棧的實際大小,瀏覽器會拋出一個錯誤終止運行。 (關(guān)注福利,關(guān)注本公眾號回復(fù)[資料]領(lǐng)取優(yōu)質(zhì)前端視頻,包括Vue、React、Node源碼和實戰(zhàn)、面試指導(dǎo)) 本周正式開始前端進(jìn)階的第一期,本周的主題是調(diào)用堆棧,今天是第3天。 本計劃一共28期,每期重點攻...
摘要:使用上一篇文章的例子來說明下自由變量進(jìn)階期深入淺出圖解作用域鏈和閉包訪問外部的今天是今天是其中既不是參數(shù),也不是局部變量,所以是自由變量。 (關(guān)注福利,關(guān)注本公眾號回復(fù)[資料]領(lǐng)取優(yōu)質(zhì)前端視頻,包括Vue、React、Node源碼和實戰(zhàn)、面試指導(dǎo)) 本周正式開始前端進(jìn)階的第二期,本周的主題是作用域閉包,今天是第7天。 本計劃一共28期,每期重點攻克一個面試重難點,如果你還不了解本進(jìn)階計...
摘要:本計劃一共期,每期重點攻克一個面試重難點,如果你還不了解本進(jìn)階計劃,點擊查看前端進(jìn)階的破冰之旅本期推薦文章深入之執(zhí)行上下文棧和深入之變量對象,由于微信不能訪問外鏈,點擊閱讀原文就可以啦。 (關(guān)注福利,關(guān)注本公眾號回復(fù)[資料]領(lǐng)取優(yōu)質(zhì)前端視頻,包括Vue、React、Node源碼和實戰(zhàn)、面試指導(dǎo)) 本周正式開始前端進(jìn)階的第一期,本周的主題是調(diào)用堆棧,今天是第二天。 本計劃一共28期,每期...
摘要:閉包面試題解由于作用域鏈機(jī)制的影響,閉包只能取得內(nèi)部函數(shù)的最后一個值,這引起的一個副作用就是如果內(nèi)部函數(shù)在一個循環(huán)中,那么變量的值始終為最后一個值。 (關(guān)注福利,關(guān)注本公眾號回復(fù)[資料]領(lǐng)取優(yōu)質(zhì)前端視頻,包括Vue、React、Node源碼和實戰(zhàn)、面試指導(dǎo)) 本周正式開始前端進(jìn)階的第二期,本周的主題是作用域閉包,今天是第8天。 本計劃一共28期,每期重點攻克一個面試重難點,如果你還不了...
閱讀 3508·2023-04-26 02:00
閱讀 3100·2021-11-22 13:54
閱讀 1711·2021-08-03 14:03
閱讀 723·2019-08-30 15:52
閱讀 3102·2019-08-29 12:30
閱讀 2430·2019-08-26 13:35
閱讀 3376·2019-08-26 13:25
閱讀 3013·2019-08-26 11:39