今天剛剛開通博客,也是第一次寫博文,略感緊張。作為一個表達能力弱弱的人來說,自己花三分鍾理解一個知識點,當別人問起時,也許需要30分鍾才只是讓別人知道自己在說什么,一點也不誇張,希望在博客上可以練習對語言的組織能力並記錄學習筆記與大家一起交流。
進入正題>>
首先,相關的概念定義如下:
1. 執行環境: 所有 JavaScript 代碼都是在一個執行環境中被執行的。執行環境是一個概念,一種機制,用來完成JavaScript運行時在作用域、生存期等方面的處理,它定義了變量或函數有權訪問的其他數據(包含了外部數據),決定他們各自的行為。包括以下分類:
全局執行環境: 全局環境是最外圍的一個執行環境,根據ECMAScript實現所在的宿主環境不同,表示執行環境的對象也不一樣,在web中,全局執行環境被認為是window對象。
函數執行環境: 每個函數都有自己的執行環境。
2. 變量對象: 每個執行環境都有一個變量對象與之關聯,執行環境中定義的所有變量及函數(只包含在當前函數內定義的函數,局部變量)都保存在這個對象中,我們編寫的代碼無法直接訪問這個對象,但解析器在處理數據時會在后台使用它。(參考下面的作用域,變量對象就是作用域為該執行環境的函數,變量的集合對象)
3. 作用域: 變量或方法有訪問權限的代碼空間,即變量或函數起作用的區域。(作用域包括全局作用域與函數作用域,沒有塊級塊作用域,即一個變量的作用域不可能是一個塊級域,至少包括最臨近的整個函數空間。)
4. 作用域鏈: 由當前環境棧中對應的變量對象組成。作用域的用途,是保證對執行環境有權訪問的所有變量和函數的有序訪問,作用域前端,始終是當前執行的代碼所在的環境對應的變量對象,下一變量對象來自包含(外部)環境,而再下一變量對象則來自下一包含環境,一直延續到全局執行環境。
以下分析將幫助大家理解上面的概念:
1. 執行環境與變量對象之間的對應關系
每個執行環境會有一個變量對象與之關聯,該變量對象保存了執行環境中定義的所有局部變量及函數,變量對象具有動態性,只有在定義變量的語句得到執行才會將該變量添加到變量對象中,如下例中
function showA () { consoloe.log(a); // 變量對象中沒有保存a alert(a); }
2. 程序執行時,環境棧與執行環境的關系
當執行流進入一個函數時,即該函數正在執行,函數的執行環境就會被推入一個環境棧中,所有處在執行流的執行環境將有次序地保存在環境棧中,在函數執行之后,棧將其執行環境彈出,把控制權交給原來的執行環境。所以在程序執行中,環境棧是不斷變化的,伴隨着執行環境的出入,如下所示:
(摘自:笨蛋的座右銘的博文)
3. 理解環境棧,執行環境與作用域鏈之間的關系
在某一時刻,環境棧中保存的執行環境是一定的,以下面的程序為例,當fn2正在執行時
function Fn1() { var a = 1; function Fn2() { var b = 2; } Fn2(); // 當程序執行到此時 } Fn1();
此時環境棧狀態如下:
此時環境棧包括:全局執行環境,fn1執行環境,fn2執行環境, 根據執行環境和變量對象具有的一一對應的關系,所以當前環境棧中執行環境和變量對象有以下對應關系
(摘自:笨蛋的座右銘的博文)
根據作用域鏈的定義,由右邊的變量對象所組成的就是當前作用域鏈,鏈的前端為Fn2執行環境對應的變量對象
4. 作用域鏈的作用(標識符解析機制)
作用域鏈的用途,是保證對執行環境有權訪問的所有變量和函數的有序訪問(包括外部數據)
標識符解析是沿着作用域鏈一級一級地搜索標識符的過程,搜索始終從作用域鏈的前端開始,然后逐級地向后回溯,直到找到標識符為止。
示例1:
var color = 'blue'; functionshowColor () { alert(color); } showColor(); // blue
在例1中,標識符解析從作用域鏈前端開始,順着作用域鏈往后找,開始在showColor()的執行環境對應的變量對象中未能找到,之外在包含環境(全局執行環境)的變量對象中找到color變量,停止搜索,所以此時的color變量引用了全局中定義的color變量,於是輸出為blue;
例2:
var color = 'blue'; function showColor () { var color = 'red'; alert(color); } showColor(); // red
在例2中,標識符解析從作用域鏈前端開始,順着作用域鏈往后找,在showColor()函數對應的執行環境對應的變量對象找到變量color,標識符解析程序立即停止搜索,此時color變量為函數中定義的color,於是輸出為red
由以上兩個例子中可以看出作用域鏈保證對執行環境有權訪問的所有變量和函數的有序訪問的原理:作用域鏈有序地保存了變量對象,由前向后,局部變量和函數往往保存在較前的變量對象中,因此被標識符解析的機會大於全局變量,也就有了局部變量會覆蓋全局變量的現象,這樣就保證了變量的有序訪問。
5. 作用域與變量對象,執行環境的關系
由定義可知,作用域即函數或變量的作用區域。
變量或函數具有作用域的原因,就是在環境中定義的變量僅保存在了該執行環境對應的對象變量中,執行環境在環境棧中彈出之后,作用域鏈中找不到該對變量對象,以下面為例
function showColor () { var color = 'red'; } showColor(); // red alert(color); // color is undefined
為什么在全局環境下showColor()內定義的變量不可訪問呢,這是因為當函數執行到該語句時,color變量保存在了showColor()環境對應的變量對象中,現在showColor()已經執行完畢,該執行環境也從環境棧中彈出並銷毀,所以此時的作用域鏈也不包括showColor()的執行環境對應的變量對象了,因為標識符解析是順着作用域鏈查找變量的,所以這個過程不再能搜索到color變量,所以color變得只有定義該變量的函數中才能訪問,具有一定范圍的作用域。
以上內容為本人的理解,錯誤之處歡迎大家積極打臉~~