JavaScript函數(shù)執(zhí)行、作用域鏈以及內(nèi)存管理詳解
前言
在我們平常編寫JavaScript代碼的時(shí)候,難免會用到函數(shù),函數(shù)里面會有各種變量,這些變量的作用的范圍,以及在使用內(nèi)存存儲這些變量時(shí),內(nèi)存管理的問題,在平時(shí)編程亦或者面試時(shí),多多少少都會遇到,所以這篇文章針對這三個問題,進(jìn)行了深入的探討。
函數(shù)執(zhí)行
首先說一下JavaScript執(zhí)行代碼的順序,JavaScript在執(zhí)行一段可執(zhí)行代碼的時(shí)候,會創(chuàng)建一個執(zhí)行上下文棧(Execution Context Stack 簡稱ECStack),執(zhí)行全局代碼時(shí)創(chuàng)建的全局執(zhí)行上下文(Global Execution Context 簡稱GEC),以及執(zhí)行函數(shù)時(shí)創(chuàng)建的函數(shù)執(zhí)行上下文(Function Execution Context 簡稱FEC),在運(yùn)行時(shí)都會按順序放入棧中,而不管是全局執(zhí)行上下文還是函數(shù)執(zhí)行上下文在創(chuàng)建時(shí)都會有一個變量對象(variable Object)。
全局執(zhí)行上下文
在JavaScript執(zhí)行全局代碼時(shí),會創(chuàng)建一個全局執(zhí)行上下文,放入執(zhí)行上下文棧,還有一個GlobalObject(GO),全局執(zhí)行上下文中會有一個變量對象(variable Object),指向GO。
在編譯階段,GO會對在全局定義的變量初始化為undefined,當(dāng)遇到函數(shù)時(shí),便會以函數(shù)名作為GO的一個屬性名,值為存儲這個函數(shù)空間的內(nèi)存地址,在這個函數(shù)空間中,會有函數(shù)的執(zhí)行體(代碼段),還會存儲這個函數(shù)父級作用域。
編譯完成后,代碼開始執(zhí)行,便會對這些變量賦值,當(dāng)然里面除了這些,還有一些全局的對象和函數(shù),比如setTimeout,Date,String等等,還有一個屬性window賦值為this,當(dāng)遇到函數(shù)時(shí),便會創(chuàng)建一個函數(shù)執(zhí)行上下文放入執(zhí)行上下文棧中。
函數(shù)執(zhí)行上下文
上文說到,代碼執(zhí)行時(shí)候,執(zhí)行到函數(shù)時(shí),會創(chuàng)建函數(shù)執(zhí)行上下文,并且函數(shù)執(zhí)行上下文放入執(zhí)行上下文棧中,同樣,在函數(shù)執(zhí)行上下文里面,會有一個VO(Variable Object)變量對象,這里的VO其實(shí)指向AO(Activation Object),這里的AO類似于GO,只不過它不是全局的,而是函數(shù)特有的,在執(zhí)行函數(shù)內(nèi)部代碼前,即編譯階段,也會將變量賦值為undefined,如果里面嵌套函數(shù),類似GO,會以函數(shù)名作為GO的一個屬性名,值為存儲這個函數(shù)空間的內(nèi)存地址,在這個函數(shù)空間中,會有函數(shù)的執(zhí)行體(代碼段),還會存儲這個函數(shù)父級作用域,然后執(zhí)行時(shí),將變量賦值,如果里面嵌套的函數(shù)被執(zhí)行,也會創(chuàng)建函數(shù)執(zhí)行上下文,并且這個函數(shù)執(zhí)行上下文放入執(zhí)行上下文棧中。
作用域鏈
其實(shí)在創(chuàng)建VO對象時(shí),也會在函數(shù)執(zhí)行上下文中創(chuàng)建作用域鏈,這個作用域鏈包括,自身的變量對象(VO)和父級作用域,當(dāng)我們查找一個變量時(shí),真實(shí)的查找路徑是沿著作用域鏈來查找
這段代碼中,顯然name會順著作用域鏈查找到“why”,然后顯然在foo 函數(shù)編譯未執(zhí)行階段,m=undefined,然后執(zhí)行,m輸出的應(yīng)該是undefined,如下圖是代碼的執(zhí)行邏輯圖。
這里的message輸出的應(yīng)該是Hello Global,foo函數(shù)在全局初始化時(shí)父級作用域已經(jīng)為全局了,然后foo函數(shù)執(zhí)行時(shí),找不到message變量便會去父級作用域去尋找,也就是全局作用域,所以輸出的是Hello Global,執(zhí)行邏輯圖如下。
上圖的輸出是undefined而不是100,就因?yàn)閒oo函數(shù)在解析時(shí)碰到return var a=100已經(jīng)認(rèn)為定義了一個a,賦值為undefined,但在執(zhí)行時(shí)卻不會執(zhí)行到這一步。
這里要提一下,如果變量在定義時(shí),未加任何約束。
比如通常來說定義一個變量是 var name=“anonymous”,let name="anonymous"如果直接寫成name=”anonymous“,在其他語言中,這肯定會報(bào)錯,但是在JavaScript中,允許這種寫法,并且這種寫法定義的變量會直接加到GO里面。
function foo(){ var a=b=10 } foo() console.log(a,b)
var a=b=10<==>等同于var a=10;b=10;
這樣的話b放入GO中,在全局輸出值為10;而a僅在函數(shù)中被定義,在全局輸出顯然會報(bào)錯。
內(nèi)存管理
不管什么樣的編程語言,在代碼的執(zhí)行過程中都是需要給它分配內(nèi)存的,不同的是某些編程語需要我們自己手動的管理內(nèi)存,某些編程語言會可以自動幫助我們管理內(nèi)存:
不管以什么樣的方式來管理內(nèi)存,內(nèi)存的管理都會有如下的生命周期:
- 第一步:分配申請你需要的內(nèi)存(申請)
- 第二步:使用分配的內(nèi)存(存放一些東西,比如對象等) ;
- 第三步:不需要使用時(shí),對其進(jìn)行釋放;
不同的編程語言對于第一步和第三步會有不同的實(shí)現(xiàn):
手動管理內(nèi)存:比如C、 C++ ,都是需要 手動來管理內(nèi)存的申請和釋放的 (malloc和free)
自動管理內(nèi)存:比如Java、JavaScript. Python. Swift、 Dart等 ,它們有自動幫助我們管理內(nèi)存;
引用計(jì)數(shù)
當(dāng)一個對象有一個引用指向它時(shí),那么這個對象的引用就+1 ,當(dāng)一個對象的引用為0時(shí),這個對象就可以被銷毀掉;
這個算法有一個很大的弊端就是會產(chǎn)生循環(huán)引用:
var obj1={info:obj2}; var obj2={info:obj1}
標(biāo)記清除
這個算法是設(shè)置一個根對象( root object) ,垃圾回收器會定期從這個根開始,找所有從根開始有引用到的對象,對于哪些沒有引用到的對象,就認(rèn)為是不可用的對象;
這個算法可以很好的解決循環(huán)弓|用的問題;
JS引擎比較廣泛的采用的就是標(biāo)記清除算法,當(dāng)然類似于V8弓|擎為了進(jìn)行更好的優(yōu)化,它在算法的實(shí)現(xiàn)細(xì)節(jié)上也會結(jié)合一些其他的算法。
以上就是JavaScript函數(shù)執(zhí)行、作用域鏈以及內(nèi)存管理詳解的詳細(xì)內(nèi)容,更多關(guān)于函數(shù)執(zhí)行、作用域鏈以及內(nèi)存管理的資料請關(guān)注腳本之家其它相關(guān)文章,希望大家以后多多支持腳本之家!
相關(guān)文章
基于javascript實(shí)現(xiàn)圖片左右切換效果
這篇文章主要為大家介紹了基于javascript實(shí)現(xiàn)圖片左右切換效果,感興趣的小伙伴們可以參考一下2016-01-01微信小程序?qū)崿F(xiàn)歷史搜索功能的全過程(h5同理)
最近在使用微信小程序開發(fā)的時(shí)候遇到了一個需求,需要實(shí)現(xiàn)歷史搜索記錄的功能,所以下面這篇文章主要給大家介紹了關(guān)于微信小程序?qū)崿F(xiàn)歷史搜索功能(h5同理)的相關(guān)資料,需要的朋友可以參考下2022-12-12JS實(shí)現(xiàn)隨機(jī)數(shù)生成算法示例代碼
JS實(shí)現(xiàn)隨機(jī)數(shù)生成算法的方法有很多,本文為大家介紹一個比較不錯的方法,代碼如下,感興趣的朋友可以參考下,希望對大家有所幫助2013-08-08如何在父窗口中得知window.open()出的子窗口關(guān)閉事件
在父窗口中得知window.open()出的子窗口關(guān)閉事件的方法有很多,在本文將為大家詳細(xì)介紹下,感興趣的朋友可以參考下2013-10-10Bootstrap實(shí)現(xiàn)模態(tài)框效果
這篇文章主要為大家詳細(xì)介紹了Bootstrap實(shí)現(xiàn)模態(tài)框效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-09-09JS實(shí)現(xiàn)頁面載入時(shí)隨機(jī)顯示圖片效果
這篇文章主要介紹了JS實(shí)現(xiàn)頁面載入時(shí)隨機(jī)顯示圖片效果,涉及javascript基于隨機(jī)數(shù)與數(shù)組的頁面元素動態(tài)修改相關(guān)操作技巧,需要的朋友可以參考下2016-09-09js防抖函數(shù)和節(jié)流函數(shù)使用場景和實(shí)現(xiàn)區(qū)別示例分析
這篇文章主要介紹了js防抖函數(shù)和節(jié)流函數(shù)使用場景和實(shí)現(xiàn)區(qū)別,結(jié)合實(shí)例形式詳細(xì)分析了js防抖函數(shù)和節(jié)流函數(shù)基本功能、定義、用法區(qū)別及操作注意事項(xiàng),需要的朋友可以參考下2020-04-04js substr、substring和slice使用說明小記
關(guān)于substr、substring和slice方法區(qū)別的文章,網(wǎng)上搜到了許多,文章內(nèi)容也基本一致。而后,我將其中一篇文章中的代碼挪到本地進(jìn)行了測試,發(fā)現(xiàn)測試結(jié)果和原文中的有些出入。2011-09-09