第三節:作用域鏈
JavaScript采用的是靜態作用域規則,也叫詞法作用域,其解析過程是按照從上到下、從左到右的順序加載,並分為兩個階段:預編譯期(預處理)和執行期。預編譯期對代碼塊中所有聲明的變量和函數進行處理。註意關鍵字:代碼塊、聲明、變量、函數。
1、代碼塊
代碼塊是指由<script>標簽分割的代碼段,JavaScript按照代碼塊來進行編譯和執行,代碼塊間相互獨立,但變量和函數共享。
<script type="text/javascript">
var msg = "我在第一個代碼塊中定義";
sayHello("張三");
alert("我可以執行嗎?");
</script>
<script type="text/javascript">
alert("第二個代碼塊中調用第一個代碼塊中的變量:\n"+msg);
</script>
執行以上代碼,首先報錯:
點擊是繼續執行:
第一個代碼塊中,因為函數sayHello沒有定義自然報錯,程序終止,所以語句alert("我可以執行嗎?")沒有執行,但是第二個代碼塊的代碼仍然可以執行,說明代碼塊間是相互獨立的。而且,第二個代碼塊可以調用第一個代碼塊的變量msg,說明代碼快間的共享性。
因此,JavaScript的執行流程是:
2、變量的預處理
預編譯期,變量只是進行了聲明但未進行初始化以及賦值。
alert(msg);
var msg = "預處理不會進行初始化";
執行結果為:
這裏顯示msg沒有賦值,說明變量msg已經聲明了,但沒有初始化賦值。如果msg沒有聲明,則應該報錯:msg未定義。
3、函數的預處理
預編譯期只是對聲明式函數進行處理。
fn();
function fn(){
alert("我是函數");
}
上面的例子說明,在預編譯期聲明式函數已經被處理了,所以即使fn()調用函數放在聲明函數前也能執行。
fn();
var fn =function(){
alert("我是函數");
}
而賦值式函數卻不會被預處理,所以fn()調用函數放在賦值函數前執行就會報錯:缺少對象。再看下面的例子:
fn();
function fn(){
alert("我是函數一");
}
function fn(){
alert("我是函數二");
}
兩個同名的聲明式函數都會被預處理,後面的函數覆蓋了前面的函數。
fn();
function fn(){
alert("我是函數一");
}
var fn = function(){
alert("我是函數二");
}
雖然兩個函數同名,但是,因為賦值式函數不會被預處理,所以執行的是第一個函數。如果fn()調用函數放在函數的定義之後,那麽:
function fn(){
alert("我是函數一");
}
var fn = function(){
alert("我是函數二");
}
fn();
在執行的時候,賦值式函數已經被處理了,後面的函數覆蓋了前面的函數,所以執行了第二個函數。
4、作用域鏈
執行下面的代碼:
function fn(){
alert(msg);
}
var msg = "我是一個變量";
fn();
正常顯示“我是一個變量”,說明內部環境可以通過作用域鏈訪問外部環境。
function fn(){
var msg = "我是一個變量";
}
alert(msg);
執行報錯“msg未定義”,說明外部環境不能訪問內部變量環境中的任何變量和函數。
function fn(){
msg = "我是一個變量";
}
fn();
alert(msg);
這段代碼成功執行,正常顯示“我是一個變量”,說明了什麽呢?
兩段代碼的函數fn有一點細微的差別,第一個函數中代碼var msg = "我是一個變量",有關鍵字var,說明在這裏聲明一個變量並初始化。而第二個函數中的代碼msg = "我是一個變量",少了關鍵字var,說明這裏給變量msg賦值。但是,在我們的代碼中並沒有聲明變量msg的語句,那麽,為什麽賦值成功了呢?而且後面的語句還以調用這個變量?
在第二段代碼中,我們執行函數fn()的時候,JavaScript引擎在變量表中找不到變量msg,就會沿著作用域鏈一直往上查找,一直到最外圍的全局執行環境,在Web瀏覽器中,全局執行環境被認為是window對象。如果都沒找到,那麽,對於讀操作,就會產生運行期錯誤;而對於寫操作,就會等價為 window.msg = "我是一個變量" ,給window對象新增了一個屬性。
所以,第一段代碼中,在函數fn內部聲明了一個變量msg,函數的外部環境無法訪問這個變量。而第二段代碼,執行函數fn,其實是給window全局對象設置了一個屬性msg,並賦值初始化,所以我們可以訪問它。其實,這段代碼標準的寫法應該是:
function fn(){
window.msg = "我是一個變量";
}
fn();
alert(window.msg);
本文出自 “老惠” 博客,轉載請與作者聯系!
第三節:作用域鏈