詳解JS內(nèi)存空間
概述
變量對象與堆內(nèi)存
var a = 20; var b = 'abc'; var c = true; var d = { m: 20 }
在很長一段時間里認(rèn)為內(nèi)存空間的概念在JS的學(xué)習(xí)中并不是那么重要??墒呛笪耶?dāng)我回過頭來重新整理JS基礎(chǔ)時,發(fā)現(xiàn)由于對它們的模糊認(rèn)知,導(dǎo)致了很多東西我都理解得并不明白。比如最基本的引用數(shù)據(jù)類型和引用傳遞到底是怎么回事兒?比如淺復(fù)制與深復(fù)制有什么不同?還有閉包,原型等等。
因此后來我才漸漸明白,想要對JS的理解更加深刻,就必須對內(nèi)存空間有一個清晰的認(rèn)知。
一、棧與堆
注:棧,也可以叫堆棧
與C/C++不同,JavaScript中并沒有嚴(yán)格意義上區(qū)分棧內(nèi)存與堆內(nèi)存。因此我們可以粗淺的理解為JavaScript的所有數(shù)據(jù)都保存在堆內(nèi)存中。但是在某些場景,我們?nèi)匀恍枰诙褩?shù)據(jù)結(jié)構(gòu)的思路進(jìn)行處理,比如JavaScript的執(zhí)行上下文(關(guān)于執(zhí)行上下文我會在下一篇文章中總結(jié))。執(zhí)行上下文在邏輯上實(shí)現(xiàn)了堆棧。因此理解堆棧數(shù)據(jù)結(jié)構(gòu)的原理與特點(diǎn)任然十分重要。
要簡單理解棧的存取方式,我們可以通過類比乒乓球盒子來分析。如下圖左側(cè)。
乒乓球盒子與棧類比
這種乒乓球的存放方式與棧中存取數(shù)據(jù)的方式如出一轍。處于盒子中最頂層的乒乓球5,它一定是最后被放進(jìn)去,但可以最先被使用。而我們想要使用底層的乒乓球1,就必須將上面的4個乒乓球取出來,讓乒乓球1處于盒子頂層。這就是??臻g先進(jìn)后出,后進(jìn)先出的特點(diǎn)。圖中已經(jīng)詳細(xì)的表明了??臻g的存儲原理。
堆存取數(shù)據(jù)的方式,則與書架與書非常相似。
書雖然也整齊的存放在書架上,但是我們只要知道書的名字,我們就可以很方便的取出我們想要的書,而不用像從乒乓球盒子里取乒乓一樣,非得將上面的所有乒乓球拿出來才能取到中間的某一個乒乓球。好比在JSON格式的數(shù)據(jù)中,我們存儲的key-value是可以無序的,因?yàn)轫樞虻牟煌⒉挥绊懳覀兊氖褂?,我們只需要關(guān)心書的名字。
二、變量對象與基礎(chǔ)數(shù)據(jù)類型
JavaScript的執(zhí)行上下文生成之后,會創(chuàng)建一個叫做變量對象的特殊對象(具體會在下一篇文章與執(zhí)行上下文一起總結(jié)),JavaScript的基礎(chǔ)數(shù)據(jù)類型往往都會保存在變量對象中。
嚴(yán)格意義上來說,變量對象也是存放于堆內(nèi)存中,但是由于變量對象的特殊職能,我們在理解時仍然需要將其于堆內(nèi)存區(qū)分開來。
基礎(chǔ)數(shù)據(jù)類型都是一些簡單的數(shù)據(jù)段,JavaScript中有5中基礎(chǔ)數(shù)據(jù)類型,分別是Undefined、Null、Boolean、Number、String?;A(chǔ)數(shù)據(jù)類型都是按值訪問,因?yàn)槲覀兛梢灾苯硬僮鞅4嬖谧兞恐械膶?shí)際的值。
三、引用數(shù)據(jù)類型與堆內(nèi)存
與其他語言不通,JS的引用數(shù)據(jù)類型,比如數(shù)組Array,它們值的大小是不固定的。引用數(shù)據(jù)類型的值是保存在堆內(nèi)存中的對象。JavaScript不允許直接訪問堆內(nèi)存中的位置,因此我們不能直接操作對象的堆內(nèi)存空間。在操作對象時,實(shí)際上是在操作對象的引用而不是實(shí)際的對象。因此,引用類型的值都是按引用訪問的。這里的引用,我們可以粗淺地理解為保存在變量對象中的一個地址,該地址與堆內(nèi)存的實(shí)際值相關(guān)聯(lián)。
為了更好的搞懂變量對象與堆內(nèi)存,我們可以結(jié)合以下例子與圖解進(jìn)行理解。
var a1 = 0; // 變量對象 var a2 = 'this is string'; // 變量對象 var a3 = null; // 變量對象 var b = { m: 20 }; // 變量b存在于變量對象中,{m: 20} 作為對象存在于堆內(nèi)存中 var c = [1, 2, 3]; // 變量c存在于變量對象中,[1, 2, 3] 作為對象存在于堆內(nèi)存中
上例圖解
因此當(dāng)我們要訪問堆內(nèi)存中的引用數(shù)據(jù)類型時,實(shí)際上我們首先是從變量對象中獲取了該對象的地址引用(或者地址指針),然后再從堆內(nèi)存中取得我們需要的數(shù)據(jù)。
理解了JS的內(nèi)存空間,我們就可以借助內(nèi)存空間的特性來驗(yàn)證一下引用類型的一些特點(diǎn)了。
在前端面試中我們常常會遇到這樣一個類似的題目
// demo01.js var a = 20; var b = a; b = 30; // 這時a的值是多少? // demo02.js var m = { a: 10, b: 20 } var n = m; n.a = 15; // 這時m.a的值是多少
在變量對象中的數(shù)據(jù)發(fā)生復(fù)制行為時,系統(tǒng)會自動為新的變量分配一個新值。var b = a執(zhí)行之后,a與b雖然值都等于20,但是他們其實(shí)已經(jīng)是相互獨(dú)立互不影響的值了。具體如圖。所以我們修改了b的值以后,a的值并不會發(fā)生變化。
demo01圖解
在demo02中,我們通過var n = m執(zhí)行一次復(fù)制引用類型的操作。引用類型的復(fù)制同樣也會為新的變量自動分配一個新的值保存在變量對象中,但不同的是,這個新的值,僅僅只是引用類型的一個地址指針。當(dāng)?shù)刂分羔樝嗤瑫r,盡管他們相互獨(dú)立,但是在變量對象中訪問到的具體對象實(shí)際上是同一個。如圖所示。
因此當(dāng)我改變n時,m也發(fā)生了變化。這就是引用類型的特性。
demo02圖解
通過內(nèi)存的角度來理解,是不是感覺要輕松很多。除此之外,我們還可以以此為基礎(chǔ),一步一步的理解JavaScript的執(zhí)行上下文,作用域鏈,閉包,原型鏈等重要概念。其他的我會在以后的文章慢慢總結(jié),敬請期待。
內(nèi)存空間管理
因?yàn)镴avaScript具有自動垃圾收集機(jī)制,所以我們在開發(fā)時好像不用關(guān)心內(nèi)存的使用問題,內(nèi)存的分配與回收都完全實(shí)現(xiàn)了自動管理。但是根據(jù)我自己的開發(fā)經(jīng)驗(yàn),了解內(nèi)存機(jī)制有助于自己清晰的認(rèn)識到自己寫的代碼在執(zhí)行過程中發(fā)生過什么,從而寫出性能更加優(yōu)秀的代碼。因此關(guān)心內(nèi)存是一件非常重要的事情。
JavaScript的內(nèi)存生命周期
1. 分配你所需要的內(nèi)存
2. 使用分配到的內(nèi)存(讀、寫)
3. 不需要時將其釋放、歸還
為了便于理解,我們使用一個簡單的例子來解釋這個周期。
var a = 20; // 在內(nèi)存中給數(shù)值變量分配空間 alert(a + 100); // 使用內(nèi)存 var a = null; // 使用完畢之后,釋放內(nèi)存空間
第一步和第二步我們都很好理解,JavaScript在定義變量時就完成了內(nèi)存分配。第三步釋放內(nèi)存空間則是我們需要重點(diǎn)理解的一個點(diǎn)。
JavaScript有自動垃圾收集機(jī)制,那么這個自動垃圾收集機(jī)制的原理是什么呢?其實(shí)很簡單,就是找出那些不再繼續(xù)使用的值,然后釋放其占用的內(nèi)存。垃圾收集器會每隔固定的時間段就執(zhí)行一次釋放操作。
在JavaScript中,最常用的是通過標(biāo)記清除的算法來找到哪些對象是不再繼續(xù)使用的,因此a = null
其實(shí)僅僅只是做了一個釋放引用的操作,讓 a 原本對應(yīng)的值失去引用,脫離執(zhí)行環(huán)境,這個值會在下一次垃圾收集器執(zhí)行操作時被找到并釋放。而在適當(dāng)?shù)臅r候解除引用,是為頁面獲得更好性能的一個重要方式。
在局部作用域中,當(dāng)函數(shù)執(zhí)行完畢,局部變量也就沒有存在的必要了,因此垃圾收集器很容易做出判斷并回收。但是全局變量什么時候需要自動釋放內(nèi)存空間則很難判斷,因此在我們的開發(fā)中,需要盡量避免使用全局變量,以確保性能問題。要詳細(xì)了解垃圾收集機(jī)制,建議閱讀《JavaScript高級編程》中的4.3節(jié)
以上就是詳解JS內(nèi)存空間的詳細(xì)內(nèi)容,更多關(guān)于JS內(nèi)存空間的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
js每隔兩秒輸出數(shù)組中的一項(xiàng)(實(shí)例)
下面小編就為大家?guī)硪黄猨s每隔兩秒輸出數(shù)組中的一項(xiàng)(實(shí)例)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-05-05JS實(shí)現(xiàn)單張或多張圖片持續(xù)無縫滾動的示例代碼
這篇文章主要介紹了JS實(shí)現(xiàn)單張或多張圖片持續(xù)無縫滾動效果,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-05-05JS+CSS實(shí)現(xiàn)隨機(jī)點(diǎn)名(實(shí)例代碼)
本文通過js html和cass代碼實(shí)現(xiàn)了隨機(jī)點(diǎn)名效果,代碼簡單易懂,非常不錯,具有一定的參考借鑒價值,需要的朋友參考下吧2019-11-11Javascript 阿拉伯?dāng)?shù)字轉(zhuǎn)化中國大寫數(shù)字的函數(shù)代碼
將阿拉伯?dāng)?shù)字轉(zhuǎn)化中國大寫數(shù)字的函數(shù)代碼,需要的朋友可以參考下。2010-04-04iframe自適應(yīng)寬度、高度 ie6 7 8,firefox 3.86下測試通過
近期需要一個iframe自適應(yīng)高度的東西,在網(wǎng)上找了很多,都不能用……一看大體的日期都是大概 2008年前后的其他近期的基本都是以前的轉(zhuǎn)載,所以只好自己動手了。2010-07-07