【進(jìn)階1-3期】JavaScript深入之內(nèi)存空間詳細(xì)圖解

(關(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)攻克一個(gè)面試重難點(diǎn),如果你還不了解本進(jìn)階計(jì)劃,點(diǎn)擊查看前端進(jìn)階的破冰之旅

本期推薦文章

前端基礎(chǔ)進(jìn)階(一):內(nèi)存空間詳細(xì)圖解 ,由于微信不能訪問外鏈,點(diǎn)擊閱讀原文就可以啦。

推薦理由

今天介紹的是JS內(nèi)存空間,了解內(nèi)存空間中的堆和棧原理對(duì)于理解JS閉包、Event Loop等有很大幫助,本文使用圖解形式,即使是第一次接觸本文的讀者也很容易理解堆棧。文末提供了一個(gè)思考題,歡迎加群討論。

閱讀筆記

堆棧的內(nèi)容和執(zhí)行順序我就不說了,前面兩篇已經(jīng)介紹過了。

【進(jìn)階1-1期】理解JavaScript 中的執(zhí)行上下文和執(zhí)行棧

【進(jìn)階1-2期】JavaScript深入之執(zhí)行上下文棧和變量對(duì)象

[圖片上傳失敗...(image-be7894-1543477607420)]

但是今天補(bǔ)充一個(gè)知識(shí)點(diǎn):某些情況下,調(diào)用堆棧中函數(shù)調(diào)用的數(shù)量超出了調(diào)用堆棧的實(shí)際大小,瀏覽器會(huì)拋出一個(gè)錯(cuò)誤終止運(yùn)行。

對(duì)于下面的遞歸就會(huì)無限制的執(zhí)行下去,直到超出調(diào)用堆棧的實(shí)際大小,這個(gè)是瀏覽器定義的。

function foo() {
    foo();
}
foo();
image

現(xiàn)在正式開始今天的主題,內(nèi)存空間詳解

棧數(shù)據(jù)結(jié)構(gòu)

棧的結(jié)構(gòu)就是后進(jìn)先出(LIFO),如果讀過前面兩篇文章應(yīng)該是相當(dāng)熟悉了。文中使用乒乓球盒子的結(jié)構(gòu)來解釋。

處于盒子中最頂層的乒乓球5,它一定是最后被放進(jìn)去,但可以最先被使用。而我們想要使用底層的乒乓球1,就必須將上面的4個(gè)乒乓球取出來,讓乒乓球1處于盒子頂層。

堆數(shù)據(jù)結(jié)構(gòu)

堆數(shù)據(jù)結(jié)構(gòu)是一種樹狀結(jié)構(gòu)。它的存取數(shù)據(jù)的方式與書架和書非常相似。我們只需要知道書的名字就可以直接取出書了,并不需要把上面的書取出來。JSON格式的數(shù)據(jù)中,我們存儲(chǔ)的key-value可以是無序的,因?yàn)轫樞虻牟煌⒉挥绊懳覀兊氖褂?,我們只需要關(guān)心書的名字。

隊(duì)列

隊(duì)列是一種先進(jìn)先出(FIFO)的數(shù)據(jù)結(jié)構(gòu),這是事件循環(huán)(Event Loop)的基礎(chǔ)結(jié)構(gòu),事件循環(huán)我們會(huì)在第8期詳解介紹。

變量的存放

首先我們應(yīng)該知道內(nèi)存中有棧和堆,那么變量應(yīng)該存放在哪里呢,??

  • 1、基本類型 --> 保存在內(nèi)存中,因?yàn)檫@些類型在內(nèi)存中分別占有固定大小的空間,通過按值來訪問?;绢愋鸵还灿?種:Undefined、Null、Boolean、Number 、String和Symbol
  • 2、引用類型 --> 保存在內(nèi)存中,因?yàn)檫@種值的大小不固定,因此不能把它們保存到棧內(nèi)存中,但內(nèi)存地址大小的固定的,因此保存在堆內(nèi)存中,在棧內(nèi)存中存放的只是該對(duì)象的訪問地址。當(dāng)查詢引用類型的變量時(shí), 先從棧中讀取內(nèi)存地址, 然后再通過地址找到堆中的值。對(duì)于這種,我們把它叫做按引用訪問。
image

在計(jì)算機(jī)的數(shù)據(jù)結(jié)構(gòu)中,棧比堆的運(yùn)算速度快,Object是一個(gè)復(fù)雜的結(jié)構(gòu)且可以擴(kuò)展:數(shù)組可擴(kuò)充,對(duì)象可添加屬性,都可以增刪改查。將他們放在堆中是為了不影響棧的效率。而是通過引用的方式查找到堆中的實(shí)際對(duì)象再進(jìn)行操作。所以查找引用類型值的時(shí)候先去查找再去查找。

幾個(gè)問題

問題1:

var a = 20;
var b = a;
b = 30;

// 這時(shí)a的值是多少?

問題2:

var a = { name: '前端開發(fā)' }
var b = a;
b.name = '進(jìn)階';

// 這時(shí)a.name的值是多少

問題3:

var a = { name: '前端開發(fā)' }
var b = a;
a = null;

// 這時(shí)b的值是多少

現(xiàn)在來解答一下,三個(gè)問題的答案分別是20、‘進(jìn)階’{ name: '前端開發(fā)' }

  • 對(duì)于問題1,a、b都是基本類型,它們的值是存儲(chǔ)在棧中的,a、b分別有各自獨(dú)立的??臻g,所以修改了b的值以后,a的值并不會(huì)發(fā)生變化。
  • 對(duì)于問題2,a、b都是引用類型,棧內(nèi)存中存放地址指向堆內(nèi)存中的對(duì)象,引用類型的復(fù)制會(huì)為新的變量自動(dòng)分配一個(gè)新的值保存在變量對(duì)象中,但只是引用類型的一個(gè)地址指針而已,實(shí)際指向的是同一個(gè)對(duì)象,所以修改b.name的值后,相應(yīng)的a.name也就發(fā)生了改變。
  • 對(duì)于問題3,首先要說明的是null是基本類型,a = null之后只是把a(bǔ)存儲(chǔ)在棧內(nèi)存中地址改變成了基本類型null,并不會(huì)影響堆內(nèi)存中的對(duì)象,所以b的值不受影響。

內(nèi)存空間管理

JavaScript的內(nèi)存生命周期是

  • 1、分配你所需要的內(nèi)存
  • 2、使用分配到的內(nèi)存(讀、寫)
  • 3、不需要時(shí)將其釋放、歸還

JavaScript有自動(dòng)垃圾收集機(jī)制,最常用的是通過標(biāo)記清除的算法來找到哪些對(duì)象是不再繼續(xù)使用的,使用a = null其實(shí)僅僅只是做了一個(gè)釋放引用的操作,讓 a 原本對(duì)應(yīng)的值失去引用,脫離執(zhí)行環(huán)境,這個(gè)值會(huì)在下一次垃圾收集器執(zhí)行操作時(shí)被找到并釋放。

在局部作用域中,當(dāng)函數(shù)執(zhí)行完畢,局部變量也就沒有存在的必要了,因此垃圾收集器很容易做出判斷并回收。但是全局變量什么時(shí)候需要自動(dòng)釋放內(nèi)存空間則很難判斷,因此在開發(fā)中,需要盡量避免使用全局變量。

思考題

var a = {n: 1};
var b = a;
a.x = a = {n: 2};

a.x     // 這時(shí) a.x 的值是多少
b.x     // 這時(shí) b.x 的值是多少

參考

前端基礎(chǔ)進(jìn)階(一):內(nèi)存空間詳細(xì)圖解

解讀 JavaScript 之引擎、運(yùn)行時(shí)和堆棧調(diào)用

JavaScript變量——棧內(nèi)存or堆內(nèi)存

往期文章查看

每周計(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)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容