【JS基礎(chǔ)】(五)JavaScript棧內(nèi)存與堆內(nèi)存

(一)堆(heap),棧(stack)與隊(duì)列(queue)

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

JavaScript中并沒(méi)有嚴(yán)格意義上區(qū)分棧內(nèi)存與堆內(nèi)存。執(zhí)行上下文的執(zhí)行順序借用了棧數(shù)據(jù)結(jié)構(gòu)的存取方式。
棧空間特點(diǎn):先進(jìn)后出,后進(jìn)先出

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

乒乓球的存放方式與棧中存取數(shù)據(jù)的方式如出一轍。處于盒子中最頂層的乒乓球5,它一定是最后被放進(jìn)去,但可以最先被使用。

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

堆數(shù)據(jù)結(jié)構(gòu)是一種樹(shù)狀結(jié)構(gòu)。它的存取數(shù)據(jù)的方式,與書(shū)架與書(shū)非常相似。我們不關(guān)心書(shū)的放置順序是怎樣的,只需知道書(shū)的名字就可以取出我們想要的書(shū)了。好比在JSON格式的數(shù)據(jù)中,我們存儲(chǔ)的key-value是可以無(wú)序的,只要知道key,就能取出這個(gè)key對(duì)應(yīng)的value

  1. 隊(duì)列

JavaScript中,理解隊(duì)列數(shù)據(jù)結(jié)構(gòu)的目的主要是為了清晰的明白事件循環(huán)(Event Loop)的機(jī)制到底是怎么回事。
隊(duì)列的特點(diǎn):先進(jìn)先出,后進(jìn)后出

隊(duì)列數(shù)據(jù)結(jié)構(gòu)

隊(duì)列是一種先進(jìn)先出FIFO)的數(shù)據(jù)結(jié)構(gòu)。正如排隊(duì)過(guò)安檢一樣,排在隊(duì)伍前面的人一定是最先過(guò)檢的人。

(二)JavaScript中的基本類型和引用類型與堆棧的關(guān)系

  1. 基本類型

Undefined、Null、Boolean、NumberString,這5中基本數(shù)據(jù)類型在內(nèi)存中分別占有固定大小的空間,他們的值保存在??臻g,是按值來(lái)訪問(wèn)的,因?yàn)?strong>可以操作保存在變量中的實(shí)際的值。

  1. 引用類型

引用數(shù)據(jù)類型的值是保存在堆內(nèi)存中的對(duì)象,值大小不固定,棧內(nèi)存中存放地址指向堆內(nèi)存中的對(duì)象,是按引用訪問(wèn)的。

棧內(nèi)存中存放的只是該對(duì)象的訪問(wèn)地址,在堆內(nèi)存中為這個(gè)值分配空間。由于這種值的大小不固定,因此不能把它們保存到棧內(nèi)存中。但內(nèi)存地址大小的固定的,因此可以將內(nèi)存地址保存在棧內(nèi)存中。 這樣,當(dāng)查詢引用類型的變量時(shí), 先從棧中讀取內(nèi)存地址, 然后再通過(guò)地址找到堆中的值。對(duì)于這種,我們把它叫做按引用訪問(wèn)

var a1 = 0;   // 變量對(duì)象
var a2 = 'this is string'; // 變量對(duì)象
var a3 = null; // 變量對(duì)象

var b = { m: 20 }; // 變量b存在于變量對(duì)象中,{m: 20} 作為對(duì)象存在于堆內(nèi)存中
var c = [1, 2, 3]; // 變量c存在于變量對(duì)象中,[1, 2, 3] 作為對(duì)象存在于堆內(nèi)存中
變量對(duì)象與堆內(nèi)存空間

JavaScript不允許直接訪問(wèn)堆內(nèi)存中的位置,因此我們不能直接操作對(duì)象的堆內(nèi)存空間。在操作對(duì)象時(shí),實(shí)際上是在操作對(duì)象的引用而不是實(shí)際的對(duì)象。為此,引用類型的值是按引用訪問(wèn)的

當(dāng)我們要訪問(wèn)堆內(nèi)存中的引用數(shù)據(jù)類型時(shí):
(1)從棧中獲取該對(duì)象的地址引用
(2)再?gòu)亩褍?nèi)存中取得我們需要的數(shù)據(jù)。

(三)復(fù)制變量值

  1. 基本類型數(shù)據(jù)的拷貝
var a = 20;
var b = a;
b = 30;

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

在變量對(duì)象中的數(shù)據(jù)發(fā)生復(fù)制行為時(shí),系統(tǒng)會(huì)自動(dòng)為新的變量分配一個(gè)新值,最后這些變量都是相互獨(dú)立互不影響的。var b = a執(zhí)行之后,ab雖然值都等于20,但是他們其實(shí)已經(jīng)是相互獨(dú)立互不影響的值了。由于基本類型是按值訪問(wèn)的,所以我們修改了b的值以后,a的值并不會(huì)發(fā)生變化。

基本數(shù)據(jù)類型的拷貝
  1. 引用類型數(shù)據(jù)的拷貝

基本類型拷貝的時(shí)候只是在內(nèi)存中又開(kāi)辟了新的空間,和它被拷貝的對(duì)象屬于 互不想干的東西,因此深淺拷貝是相對(duì)于引用類型的。

var m = { a: 10, b: 20 }
var n = m;
n.a = 15;

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

(1)引用類型的復(fù)制,同樣為新的變量n分配一個(gè)新的值,保存在棧內(nèi)存中,不同的是,這個(gè)值僅僅是引用類型的一個(gè)地址指針;
(2)他們兩個(gè)指向同一個(gè)值,也就是地址指針相同,在堆內(nèi)存中訪問(wèn)到的具體對(duì)象實(shí)際上是同一個(gè);
(3)因此改變n.a時(shí),m.a也發(fā)生了變化,這就是引用類型的特性。

引用類型的拷貝
  • 當(dāng)我們改變子對(duì)象(復(fù)制出來(lái)的新對(duì)象)時(shí),父對(duì)象(被拷貝的對(duì)象)也會(huì)跟著改變的拷貝,稱為淺拷貝。也就是說(shuō),子對(duì)象和父對(duì)象在淺拷貝的時(shí)候他們指向同一個(gè)內(nèi)存的對(duì)象。
  • 深度拷貝就是把父對(duì)象拷貝到子對(duì)象上,而且兩者的內(nèi)存和以后的操作都互不影響的拷貝!

(四)棧內(nèi)存與堆內(nèi)存總結(jié)

棧內(nèi)存 堆內(nèi)存
存儲(chǔ)基礎(chǔ)數(shù)據(jù)類型 存儲(chǔ)引用數(shù)據(jù)類型
按值訪問(wèn) 按引用訪問(wèn)
存儲(chǔ)的值大小固定 存儲(chǔ)的值大小不定,可動(dòng)態(tài)調(diào)整
由系統(tǒng)自動(dòng)分配內(nèi)存空間 由代碼進(jìn)行指定分配
空間小,運(yùn)行效率高 空間大,運(yùn)行效率相對(duì)較低
先進(jìn)后出,后進(jìn)先出 無(wú)序存儲(chǔ),可根據(jù)引用直接獲取
最后編輯于
?著作權(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)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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