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