關於作用域和作用鏈域的問題,很多文章講的都很詳細,本文屬於摘錄自己覺得對自己有價值的部分,留由后用,僅供參考,需要查看詳細信息請點擊我給出的原文鏈接查看原文件
做一個有愛的搬運工~~
-------------------------------------------------------------------------------------------------------------------------------------------
作用域
js中作用域只有一種,就是函數作用域,除此之外,還存在一種作用域叫塊作用域,但在此之前,想先說一下什么叫做塊
塊
在JAVA或者C中(因為我只學過這兩種語言),塊就是寫在{}中的語句,一般看作一條語句執行,
塊作用域(JS中不存在塊作用域的概念)
在塊中的語句對塊外是封閉的,在JAVA和C中都有這樣的特性
例如
//JAVA 代碼 public class Block { public static void main(String[] args) { if(true){ int i = 9; } System.out.println(i);//代碼報錯 } } //以上的代碼編譯時會報錯,強制編譯並執行時會拋出以下的錯誤: //Exception in thread "main" java.lang.Error: Unresolved //compilation problem: i cannot be resolved to a variable
所以,JAVA是有塊作用域的概念的,同樣的代碼,放在JS中時
//JavaScript代碼 function fun(){ if(true){ var i = 9; } return i; } fun();//輸出 9
這就說明, 在if語句中聲明的變量在if語句之后被保留在內存中了,並沒有銷毀.
需要特別說明的是:JS中,在函數中用var定義的變量是局部變量,不用var定義的變量會默認作為全局變量使用
function f1(){ var n=9; m = 10; } f1(); console.log(n);//不會輸出結果,報錯誤:ReferenceError: n is not defined, 意思是該變量未定義 console.log(m);//輸出10
關於這個說法,在很多文章中都有提及,比如 Javascript中模仿塊級作用域 : 其中對JS中作用域的解釋說:使用var關鍵字聲明變量時,這個變量會自動添加到距離最近的可用環境中。對於函數而言,這個最近的環境就是函數的局部環境。如果變量在未經聲明的情況下被初始化,則該變量會被自動添加到全局環境。
在JS中實現塊作用域的使用效果
同樣在 Javascript中模仿塊級作用域 這篇文章中,利用匿名函數來實現塊作用域的功能
(function(){ //塊級作用域 })();
//來自原文的話: 這種技術常在全局作用域中用在函數外部,來限制向全局作用域中添加過多的變量和函數。當然,只要我們臨時需要一些變量,都可以使用塊級作用域(私有作用域)。當匿名函數執行完畢,其作用域鏈立即銷毀,從而可以減少閉包占用資源問題。
在這里再貼一個也比較好的說明作用域知識的文章 js作用域問題一步步透徹理解
這篇文章中,有這樣一個例子(這個細節是我之前並沒有注意過的>>細節指數:★★★★)
function fun(){ var a = b = 10; } console.log(a);//ReferenceError: a is not defined console.log(b);//輸出: 10 /* *原文的話: var a = b = 10; 這種寫法在函數內: b其實是全局變量,a當然是局部變量 */
所以我自己給出了下面的例子
function fun(){ var a = 10, b = 10; } console.log(a);//ReferenceError: a is not defined console.log(b);//ReferenceError: b is not defined /* *a是局部變量 *b是局部變量 *我們可以很清楚的知道, var a = 10, b = 10; 這種寫法在函數內是定義局部變量的有效方法, */
-----------------------------------------------------------------------------------------------------------------------
作用鏈域
預解析(在說作用鏈域之前,我想先說一個問題---預解析,先給出引用的文章地址, 方便回溯)
解釋一下啥叫預解析(自己起的名),希望給一些初學js的提供些幫助
說法:js引擎讀取一段js代碼,首先預解析(這個名字我起的),就是逐行讀取js代碼,尋找全局變量和全局函數,遇到全局變量,把變量的值變為undefind,存在內存中,遇到全局函數,直接存在內存中,這個過程如果發現語法錯誤,預解析終止。
當預解析完成后,js引擎在從第一行開始逐行運行js代碼。
說法:js的預解析是在程序進入一個新的環境時,把該環境里的變量或函數預解析到它們能調用的環境中。即每一次預解析的單位是一個執行環境。
說法:函數預解析:
1、javascript在執行前會進行類似“預解析”的操作:首先會創建一個在當前執行環境下的活動對象,並將那些用var 聲明的變量、定義的函數設置為活動對象的屬性,但是此時這些變量的賦值都是undefined。
2、在javascript解釋執行階段,遇到變量需要解析時,會首先從當前執行環境的活動對象中查找,如果沒有找到而且該執行環境的擁有者有prototype屬性時則會從prototype鏈中查找,否則將會按照作用域鏈查找。遇到var a = …這樣的語句時會給相應的變量進行賦值(注意:變量的賦值是在解釋執行階段完成的,如果在這之前使用變量,它的值會是undefined)。
//預處理的例子 console.log(a);//輸出: undefined console.log(b);//輸出: undefined console.log(c);//輸出: function c(){return "C";} var a = "A"; var b = function(){return "B"; } function c(){return "C";}
//在語法分析階段,a保存用var進行顯示聲明的局部變量,並且置默認值為undefined,這里就是上述代碼中"console.log(a)"輸出為undefined的原因,由於代碼在語法分析階段就已經保留了標記符a,在賦值語句"var a = "A"; "執行之前a的值都是undefined,因此在"console.log(a)"的時候就顯示為undefined了。(修改引用自:(轉載)淺談JavaScript的閉包和作用域鏈)
至此,如果預解析的概念有所了解了(其實我還沒有完全理解,疑問就是:上例中的b和c的輸出結果為什么不一樣???如果有人理解,可以評論告訴我,不勝感激),就可以來說說作用鏈域的事兒了.
作用鏈域
看看這篇文章: (轉載)淺談JavaScript的閉包和作用域鏈(這篇文章講了很多機制性的東西,個人覺得比較好!!!)
原文:作用域鏈(scope chain)就是由作用域組成的鏈,是一個類似鏈狀的數據結構。作用域就是對上下文環境的數據描述。閉包和作用域鏈是緊密關系的,函數實例執行時的閉包是構成作用域鏈的基本元素。JavaScript代碼在執行前會進行語法分析,在語法分析階段,會記錄全局環境中的變量聲明和函數定義,構造函數的調用對象(Call Oject、Activation Object、Activate Object、活動對象,不同稱呼罷了)和在全局環境下的作用域鏈。
[ 關於閉包的內容我會在下一篇文章 JavaScript函數之閉包 中具體來說 ]
至於作用鏈域的其他知識點,我感覺沒有辦法總結的比文章(轉載)淺談JavaScript的閉包和作用域鏈要好,所以,不多說,看原文!
--------------------------------------------------------------------------------------------------------------------------
參考文章: