要理解變數的範圍範圍就得先理解範圍鏈
用var關鍵字聲明一個變數時,就是為該變數所在的對象添加了一個屬性。
範圍鏈:由於js的變數都是對象的屬性,而該對象可能又是其它對象的屬性,而所有的對象都是window對象的屬性,所以這些對象的關係可以看作是一條鏈
鏈頭就是變數所處的對象,鏈尾就是window對象
看下面的代碼:
複製代碼 代碼如下:
function t() {
var a;
function t2() {
var b;
}
}
js中函數也是對象,所以變數a所在的對象是t,t又在window對象中,所以a的範圍鏈如下
t--window
那麼b所以在的對象即t2,t2又包含在t中,t又在window對象,所以b的範圍鏈如下
t2--t--window
明白了範圍鏈下面就開始變數的範圍分析了
1 javascript 沒有var的變數都為全域變數,且為window對象的屬性
複製代碼 代碼如下:
function test1() {
//執行這個句的時候它會找範圍對象,這個函數就是範圍鏈中的第一個對象,但這個對象中沒有相關的var語句
//於裡就找範圍鏈的第二個對象,即全域對象,而全域對象中也沒有相關的var語句
//由於沒有相關的var語句,js隱式在函數地聲明了變數即var all;
all = 30;
alert(all);
}
test1();
alert(all);
alert(window.all);
2 函數內(函數內的函數除外)定義的變數在整個函數內部都有效
複製代碼 代碼如下:
function test2() {
var t = 0;
//在for的條件裡定義變數,這個變更的範圍鏈對象是這個函數
//因此在整個的函數裡它是有效
for (var i = 0; i < 5; i++) {
t += i;
}
alert(i);
}
test2();
3 函數內部的變數取代全域同名變數
複製代碼 代碼如下:
var t = "bb";
function test() {
//執行t的時候,它會先找範圍鏈對象,由於它定義在函數內部,所以這個函數就是它的範圍鏈的第一個對象
//而在這個對象裡又有t的定義,所以t就是局部變數了,它替換了全域變數t
//t只是此時有定義,但並沒有賦值,賦值在下一行,所以這裡輸出了undefined
alert(t);
var t = "aa";
alert(t);
}
test();
4 沒塊的範圍
複製代碼 代碼如下:
if (true) {
//在塊中定義了一個變數,它的範圍鏈的第一個對象就是全域對象window
var tmp = 0;
}
//tmp的範圍鏈的第一個對象就是全域對象window,而上面又有全域對象中相關的var語句,因此輸出0
alert(tmp);
以下內容來自讀網上部落格的總結,當筆記使用,只記重點,同時非常感謝樂於分享的博主們,是你們讓我站在了巨人的肩旁上!
1、
複製代碼 代碼如下:
var temp = (function(){
var name ="test";
return function(){
alert(name);
}
})();
以上代碼片斷是我們jser經常見到的寫法,是傳說中的閉包。 眾所周知:調用 temp();會彈出 “ test”;該過程可以有以下三條理論作為依據來解釋:
1)js 範圍只和函數的界定符相關,函數與函數的嵌套形成了範圍鏈;
2)範圍鏈的建立規則是複製上一層環境的範圍鏈,並將指向本環境變數對象的指標放到鏈首;
3)在Javascript中,如果一個對象不再被引用,那麼這個對象就會被GC回收。如果兩個對象互相引用,而不再被第3者所引用,那麼這兩個互相引用的對象也會被回收。
如果看了以上3條還不明白,可看接下來結合理論對代碼的詳細解釋:
首先外層函數執行完,被銷毀;但是外層函數的範圍鏈被複製到內層函數的範圍鏈裡,組成內層函數的範圍鏈的一部分,記住是複製,不是引用(依據第2條),所以內層函數仍然可以訪問到 name;由於 返回的內層函數被 temp 引用,所以當外層函數執行完被銷毀後,內層函數雖然作為外層函數的一部分,但是依然存在,正如第3條依據那樣,它被第三者引用了;傳說中的閉包也就是這個理