欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

JavaScript函數(shù)執(zhí)行、作用域鏈以及內(nèi)存管理詳解

 更新時間:2023年01月08日 11:09:11   作者:既白biu  
這篇文章主要介紹了JavaScript函數(shù)執(zhí)行、作用域鏈以及內(nèi)存管理的知識,文章內(nèi)容非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

前言

在我們平常編寫JavaScript代碼的時候,難免會用到函數(shù),函數(shù)里面會有各種變量,這些變量的作用的范圍,以及在使用內(nèi)存存儲這些變量時,內(nèi)存管理的問題,在平時編程亦或者面試時,多多少少都會遇到,所以這篇文章針對這三個問題,進行了深入的探討。

函數(shù)執(zhí)行

首先說一下JavaScript執(zhí)行代碼的順序,JavaScript在執(zhí)行一段可執(zhí)行代碼的時候,會創(chuàng)建一個執(zhí)行上下文棧(Execution Context Stack 簡稱ECStack),執(zhí)行全局代碼時創(chuàng)建的全局執(zhí)行上下文(Global Execution Context 簡稱GEC),以及執(zhí)行函數(shù)時創(chuàng)建的函數(shù)執(zhí)行上下文(Function Execution Context 簡稱FEC),在運行時都會按順序放入棧中,而不管是全局執(zhí)行上下文還是函數(shù)執(zhí)行上下文在創(chuàng)建時都會有一個變量對象(variable Object)。

全局執(zhí)行上下文

在JavaScript執(zhí)行全局代碼時,會創(chuàng)建一個全局執(zhí)行上下文,放入執(zhí)行上下文棧,還有一個GlobalObject(GO),全局執(zhí)行上下文中會有一個變量對象(variable Object),指向GO。

在編譯階段,GO會對在全局定義的變量初始化為undefined,當遇到函數(shù)時,便會以函數(shù)名作為GO的一個屬性名,值為存儲這個函數(shù)空間的內(nèi)存地址,在這個函數(shù)空間中,會有函數(shù)的執(zhí)行體(代碼段),還會存儲這個函數(shù)父級作用域。

編譯完成后,代碼開始執(zhí)行,便會對這些變量賦值,當然里面除了這些,還有一些全局的對象和函數(shù),比如setTimeout,Date,String等等,還有一個屬性window賦值為this,當遇到函數(shù)時,便會創(chuàng)建一個函數(shù)執(zhí)行上下文放入執(zhí)行上下文棧中。

函數(shù)執(zhí)行上下文

上文說到,代碼執(zhí)行時候,執(zhí)行到函數(shù)時,會創(chuàng)建函數(shù)執(zhí)行上下文,并且函數(shù)執(zhí)行上下文放入執(zhí)行上下文棧中,同樣,在函數(shù)執(zhí)行上下文里面,會有一個VO(Variable Object)變量對象,這里的VO其實指向AO(Activation Object),這里的AO類似于GO,只不過它不是全局的,而是函數(shù)特有的,在執(zhí)行函數(shù)內(nèi)部代碼前,即編譯階段,也會將變量賦值為undefined,如果里面嵌套函數(shù),類似GO,會以函數(shù)名作為GO的一個屬性名,值為存儲這個函數(shù)空間的內(nèi)存地址,在這個函數(shù)空間中,會有函數(shù)的執(zhí)行體(代碼段),還會存儲這個函數(shù)父級作用域,然后執(zhí)行時,將變量賦值,如果里面嵌套的函數(shù)被執(zhí)行,也會創(chuàng)建函數(shù)執(zhí)行上下文,并且這個函數(shù)執(zhí)行上下文放入執(zhí)行上下文棧中。

作用域鏈

其實在創(chuàng)建VO對象時,也會在函數(shù)執(zhí)行上下文中創(chuàng)建作用域鏈,這個作用域鏈包括,自身的變量對象(VO)和父級作用域,當我們查找一個變量時,真實的查找路徑是沿著作用域鏈來查找

這段代碼中,顯然name會順著作用域鏈查找到“why”,然后顯然在foo 函數(shù)編譯未執(zhí)行階段,m=undefined,然后執(zhí)行,m輸出的應(yīng)該是undefined,如下圖是代碼的執(zhí)行邏輯圖。

這里的message輸出的應(yīng)該是Hello Global,foo函數(shù)在全局初始化時父級作用域已經(jīng)為全局了,然后foo函數(shù)執(zhí)行時,找不到message變量便會去父級作用域去尋找,也就是全局作用域,所以輸出的是Hello Global,執(zhí)行邏輯圖如下。

上圖的輸出是undefined而不是100,就因為foo函數(shù)在解析時碰到return var a=100已經(jīng)認為定義了一個a,賦值為undefined,但在執(zhí)行時卻不會執(zhí)行到這一步。

這里要提一下,如果變量在定義時,未加任何約束。

比如通常來說定義一個變量是 var name=“anonymous”,let name="anonymous"如果直接寫成name=”anonymous“,在其他語言中,這肯定會報錯,但是在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ù)中被定義,在全局輸出顯然會報錯。

內(nèi)存管理

不管什么樣的編程語言,在代碼的執(zhí)行過程中都是需要給它分配內(nèi)存的,不同的是某些編程語需要我們自己手動的管理內(nèi)存,某些編程語言會可以自動幫助我們管理內(nèi)存:

不管以什么樣的方式來管理內(nèi)存,內(nèi)存的管理都會有如下的生命周期:

  • 第一步:分配申請你需要的內(nèi)存(申請)
  • 第二步:使用分配的內(nèi)存(存放一些東西,比如對象等) ;
  • 第三步:不需要使用時,對其進行釋放;

不同的編程語言對于第一步和第三步會有不同的實現(xiàn):

手動管理內(nèi)存:比如C、 C++ ,都是需要 手動來管理內(nèi)存的申請和釋放的 (malloc和free)

自動管理內(nèi)存:比如Java、JavaScript. Python. Swift、 Dart等 ,它們有自動幫助我們管理內(nèi)存;

引用計數(shù)

當一個對象有一個引用指向它時,那么這個對象的引用就+1 ,當一個對象的引用為0時,這個對象就可以被銷毀掉;

這個算法有一個很大的弊端就是會產(chǎn)生循環(huán)引用:

var obj1={info:obj2};
var obj2={info:obj1}

標記清除

這個算法是設(shè)置一個根對象( root object) ,垃圾回收器會定期從這個根開始,找所有從根開始有引用到的對象,對于哪些沒有引用到的對象,就認為是不可用的對象;

這個算法可以很好的解決循環(huán)弓|用的問題;

JS引擎比較廣泛的采用的就是標記清除算法,當然類似于V8弓|擎為了進行更好的優(yōu)化,它在算法的實現(xiàn)細節(jié)上也會結(jié)合一些其他的算法。

以上就是JavaScript函數(shù)執(zhí)行、作用域鏈以及內(nèi)存管理詳解的詳細內(nèi)容,更多關(guān)于函數(shù)執(zhí)行、作用域鏈以及內(nèi)存管理的資料請關(guān)注腳本之家其它相關(guān)文章,希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論