深入理解JS中的變數作用域
文章轉載http://blog.csdn.net/beijiguangyong/article/details/8301707點選開啟連結
在JS當中一個變數的作用域(scope)是程式中定義這個變數的區域。變數分為兩類:全域性(global)的和區域性的。其中全域性變數的作用域是全域性性的,即在JavaScript程式碼中,它處處都有定義。而在函式之內宣告的變數,就只在函式體內部有定義。它們是區域性變數,作用域是區域性性的。函式的引數也是區域性變數,它們只在函式體內部有定義。
我們可以藉助JavaScript的作用域鏈(scope chain)更好地瞭解變數的作用域。每個JavaScript執行環境都有一個和它關聯在一起的作用域鏈。這個作用域鏈是一個物件列表或物件鏈。當JavaScript程式碼需要查詢變數x的值時(這個過程叫做變數解析(variable name resolution)),它就開始檢視該鏈的第一個物件。如果那個物件有一個名為x的屬性,那麼就採用那個屬性的值。如果第一個物件沒有名為x的屬性,JavaScript就會繼續查詢鏈中的第二個物件。如果第二個物件仍然沒有名為x的屬性,那麼就繼續查詢下一個物件,以此類推。如果查詢到最後(指頂層程式碼中)不存在這個屬性,那麼這個變數的值就是未定義的。
以上的過程並不是我們所熟悉的順序結構,但大致與順序結構類似只不過是將作用域當作一個整體來看待而已。整個過程如上圖所示
JS作用域例項
程式碼一(平淡的不能再平淡的程式碼)
var i=10;
function a() {
alert(i);
};
a();
程式碼二
var i=10;
function a() {
alert(i);
var i = 2;
};
a();
根據“多年”的程式設計經驗你可能覺得這兩個程式碼輸出是一樣的,但是事實卻是第一個程式碼正常輸出了變數的值----10,而第二個程式碼輸出的卻是undefined。也許很多人理解不了,但是根據前面的作用域鏈的圖我們就很好理解了。
作用域鏈圖中很明確的表示出:在變數解析過程中首先查詢區域性的作用域,然後查詢上層作用域。在程式碼一的函式當中沒有定義變數i,於是查詢上層作用域(全域性作用域),進而進行輸出其值。但是在程式碼二的函式內定義了變數i(無論是在alter之後還是之前定義變數,都認為在此作用域擁有變數i),於是不再向上層的作用域進行查詢,直接輸出i。但是不幸的是此時的區域性變數i並沒有賦值,所以輸出的是undefined。
《JavaScript權威指南》中提出的“沒有塊級作用域”實際上就是上述的意思。很多時候認為懂了、理解了,其實沒有懂,細細的研究一番之後看回過頭來再書中那加粗的文字,頓時恍然大悟,其實人家書裡說的挺清楚的嘛!
程式碼三 某知名公司筆試題
<script type="text/javascript">
var a,b;
(function(){
alert(a);
alert(b);
var a = b = 3;
alert(a);
alert(b);
})();
alert(a);
alert(b);
</script>
結果輸出 undefined undefined 3 3 undefined 3
程式碼等價於
<script type="text/javascript">
var a,b;
(function(){
alert(a);
alert(b);
var a = 3;
b = 3;
alert(a);
alert(b);
})();
alert(a);
alert(b);
</script>
這主要是Js中沒有用var宣告的變數都是全域性變數,而且是頂層物件的屬性。
作用域鏈說明
程式碼四:
<script type="text/javascript">
name="Hello";
function t(){
var name="world";
function s(){
var name="welcome";
console.log(name);
}
function ss(){
console.log(name);
}
s();
ss();
}
t();
</script>
函式輸出welcome world
當執行s時,將建立函式s的執行環境(呼叫物件),並將該物件置於連結串列開頭,然後將函式t的呼叫物件連結在之後,最後是全域性物件。然後從連結串列開頭尋找變數name,很明顯
name是"welcome"。
但執行ss()時,作用域鏈是: ss()->t()->window,所以name是”world"
程式碼五:一個很容易犯錯的例子:<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" >
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
<title>ajax</title>
<style type="text/css">
</style>
<script type="text/javascript">
function buttonInit(){
for(var i=1;i<4;i++){
var b=document.getElementById("button"+i);
b.addEventListener("click",function(){ alert("Button"+i);},false);
}
}
window.onload=buttonInit;
</script>
</head>
<body>
<button id="button1">Button1</button>
<button id="button2">Button2</button>
<button id="button3">Button3</button>
</body>
</html>
當文件載入完畢,給幾個按鈕註冊點選事件,當我們點選按鈕時,會彈出什麼提示框呢?
很容易犯錯,對是的,三個按鈕都是彈出:"Button4",你答對了嗎?
當註冊事件結束後,i的值為4,當點選按鈕時,事件函式即function(){ alert("Button"+i);}這個匿名函式中沒有i,根據作用域鏈,所以到buttonInit函式中找,此時i的值為4,
所以彈出”button4“。