介紹一下JavaScript作用域鏈?
- 什么是作用域鏈
- 每個函數都有自己的 執(zhí)行環(huán)境 ,當執(zhí)行流執(zhí)行到一個函數時,函數的環(huán)境就會被推入到一個 環(huán)境棧 中,而在函數執(zhí)行之后,棧將其環(huán)境彈出,將控制權返給之前的執(zhí)行環(huán)境;
- 當代碼在一個環(huán)境中訪問時,會創(chuàng)建變量對象的一個 作用域鏈 。作用域鏈的用途,就是 保證對執(zhí)行環(huán)境有權訪問的所有變量和函數的有序訪問 ;作用域鏈的前端,始終都是 當前執(zhí)行代碼所在環(huán)境的變量對象 。如果這個變量是函數,則將其活動對象作為變量對象;活動對象在最開始時只包含一個變量,即argumnets對象(這個對象在全局環(huán)境中是不存在的)。作用域鏈中的下一個變量對象來自包含環(huán)境,在下一個變量對象則來自再下一個包含環(huán)境。這樣,一直延續(xù)到 全局執(zhí)行環(huán)境。全局執(zhí)行環(huán)境始終是作用域鏈中的最后一個對象。
const color = 'red'
const function = sayColor(){
const innerColor = 'blue'
console.log(color)
}
sayColor() // 'red'
- 在上面的例子中,sayColor()的作用域鏈中包含兩個變量對象,他自己的活動對象(其中包含著argumnets對象)和全局環(huán)境的變量對象
- 可以在函數內部訪問到變量color,就是因為可以在這個作用域鏈中找到他;
- 內部環(huán)境可以通過作用域鏈訪問所有的外部環(huán)境,但是外部環(huán)境不能訪問內部環(huán)境的任何變量和函數;這些環(huán)境之間的聯(lián)系是線性的,有次序的,每個環(huán)境都可以向上搜索作用域鏈,以查詢變量和函數名; 但是任何環(huán)境都不能向下搜索作用域鏈而進入另一個執(zhí)行環(huán)境;
- 延長作用域鏈
- 雖然執(zhí)行環(huán)境的類型只有兩種,全局和局部(函數),但還是有其他方法來延長作用域鏈,這么說是因為,可以通過一些手段,在作用域鏈的前端,臨時增加一個變量對象,這個變量對象會在代碼執(zhí)行之后被移除。這兩種情況下會發(fā)生這種現象,具體來說,當執(zhí)行流進行到下面任何一個語句時,作用域鏈就會得到加長;
- try-catch語句的catch塊
- with語句
- 這兩個方法都會在作用域鏈的前端增加一個變量對象。對with語句來說,會將指定的對象添加到作用域鏈中。對ctach語句來說,會創(chuàng)建一個新的變量對象,其中包含的是被拋出的錯誤對象的變量聲明;
function basicUrl(){
var qs = 'haha';
with(location){
return url = href + qs
}
return url;
}
basicUrl()
// "http://localhost:4000/Blog/haha"
- 在此,with 語句接收的是location對象,因此其變量對象中包含了location的所有屬性和方法,而這個變量對象被添加到了作用域鏈的前端。basicUrl()函數中定義了一個變量qs,當在with語句中引用變量href時(實際引用的是location.href),可以在當前執(zhí)行環(huán)境的變量對象中找到;當引用變量qs時,引用的則是basicUrl中定義的
qs,而該變量位于函數環(huán)境的變量對象中。至于with語句內部,則定義了一個名為url的變量,因此url就成為了函數執(zhí)行環(huán)境的一部分,所以可以作為函數的值被返回。
function compare(value1, value2) {
if (value1 > value2) {
return 1;
} else if (value1 < value2) {
return -1;
} else {
return 0;
}
}
const result = compare(2, 3);
- 全局環(huán)境的變量對象始終存在,而象compare()函數這樣的局部環(huán)境的變量對象,僅在函數執(zhí)行的過程中存在。在創(chuàng)建compare()函數時,會創(chuàng)建一個預先包含全局變量對象的作用域鏈,這個作用域鏈被保存在內部的 [[Scope]] 屬性中。當調用compare()函數時,回味函數創(chuàng)建一個執(zhí)行環(huán)境,然后通過復制 [[Scope]] 屬性中的對象,構建起執(zhí)行環(huán)境的作用域鏈。此后,又有一個活動對象(在此作為變量對象使用)被推入執(zhí)行環(huán)境作用域鏈的前端。
- 對于這個例子中的compare()函數的執(zhí)行環(huán)境而言,其作用域鏈包含兩個變量對象,本地活動對象和全局變量對象。
- 作用域鏈本質上是一個指向變量對象的指針列表,他只引用但不實際包含變量對象
- 無論什么時候在函數中搜索一個變量時,就會從作用域鏈中搜索具有相應名字的變量,一般來講,當函數執(zhí)行完畢后,局部活動對象就會被銷毀,內存中僅保存全局作用域(全局環(huán)境的活動對象),但是閉包的情況有所不同