執(zhí)行環(huán)境
執(zhí)行環(huán)境(excecution context),也叫做執(zhí)行上下文抗楔,定義了變量或函數(shù)有權(quán)訪問的其他數(shù)據(jù)惯雳,決定了它們各自的行為。每個只i系那個環(huán)境都有一個與之關(guān)聯(lián)的變量對象妥箕,環(huán)境中定義的所有變量和函數(shù)都保存在這個對象中滥酥。
全局執(zhí)行環(huán)境是最外圍的一個執(zhí)行環(huán)境,在Web瀏覽器中畦幢,全局執(zhí)行環(huán)境被認為是window對象坎吻。
每個函數(shù)都有子句的只想能夠環(huán)境。當一個函數(shù)被調(diào)用時宇葱,函數(shù)的環(huán)境就會被推入一個環(huán)境棧中瘦真,而在函數(shù)執(zhí)行之后,棧將環(huán)境他出黍瞧,吧控制權(quán)返回給之前的執(zhí)行環(huán)境诸尽。
看下面這一段代碼
console.log(a) // Uncaught ReferenceError: a is not defined
console.log(a) // undefined
var a = 10;
console.log(a) // 10
在一段代碼執(zhí)行之前,JS就已經(jīng)做了一些“準備工作”印颤,包括對變量的聲明您机,這段代碼里所有變量的聲明都被提前到代碼一句一句執(zhí)行之前完成,但僅僅是聲明,而沒有賦值往产。變量復制是在執(zhí)行到賦值語句的時候進行的被碗。
console.log(fn1) ; //? fn1() {}
function fn1() {}
console.log(fn2); // undefined
var fn2 = function() {};
函數(shù)聲明和函數(shù)表達式的待遇是不同的,對于函數(shù)聲明仿村,準備階段會同時為函數(shù)名賦值锐朴,而對函數(shù)表達式,就只是聲明了蔼囊。
全局環(huán)境下的執(zhí)行環(huán)境中焚志,包括
- 變量,函數(shù)表達式 —— 聲明畏鼓,默認值為undefined
- this —— 賦值
- 函數(shù)聲明 —— 賦值
每次調(diào)用一個函數(shù) 回铛,也會創(chuàng)建這個函數(shù)的執(zhí)行環(huán)境,相比于全局環(huán)境的執(zhí)行環(huán)境葫男,還多了這些:
- 參數(shù) —— 賦值
- arguments —— 賦值
- 確定變量的作用域
作用域鏈
當代碼在一個環(huán)境中運行是矫渔,會創(chuàng)建變量對象的一個作用域鏈。作用域鏈的用途是让禀,保證對執(zhí)行環(huán)境有權(quán)訪問的所有變量和函數(shù)的有序訪問挑社。
在一個函數(shù)中可以訪問函數(shù)之外定義的變量,這就和作用域與作用域鏈有關(guān)
var a = 10;
function fn1() {
console.log(a); // 10
}
function fn2() {
var a = 20;
fn1(); // 10巡揍,而不是20
}
函數(shù)在訪問一個變量的時候:
- 先從自己內(nèi)部找變量
- 如果找不到痛阻,再從創(chuàng)建當前函數(shù)所在的作用域去找, 以此往上
- 如果一直到全局作用域還沒有找到,說明變量未定義腮敌,報錯
注意要到創(chuàng)建這個函數(shù)的那個作用域中取值——是“創(chuàng)建”阱当,而不是“調(diào)用”。函數(shù)在定義的時候(不是調(diào)用的時候)糜工,就已經(jīng)確定了函數(shù)體內(nèi)部自由變量的作用域弊添。
var a = 10;
function fn() {
var b = 20;
function bar() {
console.log(a + b);
}
return bar;
}
var x = fn(),
b = 200;
x(); // 30
以上代碼中:第13行,fn()返回的是bar函數(shù)捌木,賦值給x油坝。執(zhí)行x(),即執(zhí)行bar函數(shù)代碼钮莲。取b的值時免钻,直接在fn作用域取出。取a的值時崔拥,試圖在fn作用域取极舔,但是取不到,只能轉(zhuǎn)向創(chuàng)建fn的那個作用域中去查找链瓦,結(jié)果找到了拆魏。