標籤:
關於範圍和作用鏈域的問題,很多文章講的都很詳細,本文屬於摘錄自己覺得對自己有價值的部分,留由後用,僅供參考,需要查看詳細資料請點擊我給出的原文連結查看原檔案
做一個有愛的搬運工~~
-------------------------------------------------------------------------------------------------------------------------------------------
範圍
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 definedconsole.log(b);//輸出: 10/* *原文的話: var a = b = 10; 這種寫法在函數內: b其實是全域變數,a當然是局部變數 */
所以我自己給出了下面的例子
function fun(){ var a = 10, b = 10;}console.log(a);//ReferenceError: a is not definedconsole.log(b);//ReferenceError: b is not defined/* *a是局部變數 *b是局部變數 *我們可以很清楚的知道, var a = 10, b = 10; 這種寫法在函數內是定義局部變數的有效方法, */
-----------------------------------------------------------------------------------------------------------------------
作用鏈域
預解析(在說作用鏈域之前,我想先說一個問題---預解析,先給出引用的文章地址, 方便回溯)
解釋一下啥叫預解析(自己起的名),希望給一些初學js的提供些協助
說法:js引擎讀取一段js代碼,首先預解析(這個名字我起的),就是逐行讀取js代碼,尋找全域變數和全域函數,遇到全域變數,把變數的值變為undefind,存在記憶體中,遇到全域函數,直接存在記憶體中,這個過程如果發現語法錯誤,預解析終止。
當預解析完成後,js引擎在從第一行開始逐行運行js代碼。
js的預解析
說法:js的預解析是在程式進入一個新的環境時,把該環境裡的變數或函數預解析到它們能調用的環境中。即每一次預解析的單位是一個執行環境。
轉:javascript(js)預解析原理
說法:函數預解析:
1、javascript在執行前會進行類似“預解析”的操作:首先會建立一個在當前執行環境下的使用中的物件,並將那些用var 聲明的變數、定義的函數設定為使用中的物件的屬性,但是此時這些變數的賦值都是undefined。
2、在javascript解釋執行階段,遇到變數需要解析時,會首先從當前執行環境的使用中的物件中尋找,如果沒有找到而且該執行環境的擁有者有prototype屬性時則會從prototype鏈中尋找,否則將會按照範圍鏈尋找。遇到var a = …這樣的語句時會給相應的變數進行賦值(注意:變數的賦值是在解釋執行階段完成的,如果在這之前使用變數,它的值會是undefined)。
//預先處理的例子console.log(a);//輸出: undefinedconsole.log(b);//輸出: undefinedconsole.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的閉包和範圍鏈要好,所以,不多說,看原文!
--------------------------------------------------------------------------------------------------------------------------
參考文章:
Javascript中模仿塊級範圍
js範圍問題一步步透徹理解
解釋一下啥叫預解析(自己起的名),希望給一些初學js的提供些協助
js的預解析
轉:javascript(js)預解析原理
(轉載)淺談JavaScript的閉包和範圍鏈
JavaScript函數之範圍 / 作用鏈域 / 預解析