js 執(zhí)行上下文,作用域,變量內(nèi)存管理
JS中變量的定義在內(nèi)存中包括三個部分:
* 變量標示 (變量標示存儲在內(nèi)存的棧區(qū))
* 變量值 (比如上面中的Str的值souvenir或者是obj1對象的指向堆區(qū)地址,這個值也是存儲在棧區(qū))
* 對象 (對象存儲在堆區(qū))
也就是說,對于基本數(shù)據(jù)類型來說,只使用了內(nèi)存的棧區(qū)。
執(zhí)行上下文堆棧
三種作用域
全局作用域、函數(shù)作用域、eval作用域(不常用,不做介紹)。
JavaScript中的運行環(huán)境大概包括三種情況。
- 全局環(huán)境:JavaScript代碼運行起來會首先進入該環(huán)境
- 函數(shù)環(huán)境:當函數(shù)被調(diào)用執(zhí)行時,會進入當前函數(shù)中執(zhí)行代碼
- eval
當javascript代碼文件被瀏覽器載入后,首先進行上下文環(huán)境的準備工作。會將全局的執(zhí)行上下文壓入執(zhí)行上下文棧的底部。
當在全局上下文中調(diào)用執(zhí)行一個函數(shù)時,程序流就進入該被調(diào)用函數(shù)內(nèi),此時引擎就會為該函數(shù)創(chuàng)建一個新的執(zhí)行上下文,并且將其壓入到執(zhí)行上下文棧。棧頂永遠都是當前正在執(zhí)行的上下文,執(zhí)行完畢后該上下文就會從棧頂被彈出,直到回到全局的上下文。全局上下文在瀏覽器窗口關(guān)閉后出棧。
注意:函數(shù)中,遇到return能直接終止可執(zhí)行代碼的執(zhí)行,因此會直接將當前上下文彈出棧。
函數(shù)作用域中this的是在JS運行時才能確定,而全局作用域中的this在準備上下文環(huán)境的時候就確定了(window)。
執(zhí)行上下文的建立過程
- 建立階段(發(fā)生在當調(diào)用一個函數(shù)時,但是在執(zhí)行函數(shù)體內(nèi)的具體代碼以前) ○ 建立變量,函數(shù),arguments對象,參數(shù) ○ 建立作用域鏈 ○ 確定this的值
- 代碼執(zhí)行階段: ○ 變量賦值,函數(shù)引用,執(zhí)行其它代碼
建立階段:
- 建立variableObject對象: i. 為函數(shù)的參數(shù)和arguments對象賦值。建立arguments對象,檢查當前上下文中的參數(shù),建立該對象下的屬性以及屬性值 ii. 檢查當前函數(shù)聲明和變量。將函數(shù)名和變量名作為variableObject的一個屬性 存入,屬性值都設(shè)為undefined。 為函數(shù)聲明直接賦值字符串
- 初始化作用域鏈 ○ 確定上下文中this的指向?qū)ο?/li>
執(zhí)行階段:
○ 執(zhí)行函數(shù)體中的代碼,一行一行地運行代碼,給variableObject中的變量屬性賦值。
通俗的講,執(zhí)行上下文準備工作就是在執(zhí)行代碼前,把將要用到的所有的變量都事先拿出來,有的直接賦值了,有的先用undefined占個空。
//將全局變量的值設(shè)為undefined
console.log(a); //undefined
var a = 1;
//將window對象賦給this
console.log(this);
//將函數(shù)表達式的值設(shè)為undefined
console.log(fn1);//undefined
var fn1 = function(){
//……
}
//為函數(shù)聲明直接賦值
console.log(fn2); //輸出函數(shù)的代碼
function fn2(){
//……
}
//函數(shù)作用域
function fn3(b,c){
//確定自由變量的作用域
console.log(a);
//為局部變量賦上undefined
console.log(x);//undefined
var x = 123;
//為函數(shù)的參數(shù)賦值
console.log(b,c); //2,3
//為arguments賦值
console.log(arguments); //[2,3]
}
fn3(2,3);