《JavaScript權威指南》第6版第三章第10節:
一個變量的作用域(scope)是程序源代碼中定義這個變量的區域。全局變量擁有全局作用域,在JavaScript代碼中的任何地方都是有定義的。然而在函數內聲明的變量只在函數體內有定義。它們是局部變量,作用域也是局部的。函數參數也是局部變量,它們只在函數體內有定義。
在函數體內,局部變量的優先級高於同名的全局變量。如果在函數內聲明的一個局部變量或函數參數中帶有的變量和全局變量重名,那麼全局變量就被局部變量所遮蓋。
var scope = "global"; //聲明一個全局變量 function checkscope(){ var scope = "local"; //聲明一個同名的局部變量 return scope; //返回局部變量的值,而不是全局變量的值 } checkscope(); // ==>"local"
儘管在全局作用域編寫代碼時可以不寫var 語句,但聲明局部變量時則必須使用var。如:
scope = "global"; //聲明一個全局變量,不用var function checkscope(){ scope = "local"; //糟糕!我們剛修改了全局變量的值! myscope = "local"; // 這是顯式聲明瞭一個新的全局變量 return [scope,myscope]; // 返回兩個值 } checkscope(); // ==>["local","local"]:產生了副作用 scope; //"local":全局變量修改了 myscope; //"local":全局命名空間搞亂了
函數定義是可以嵌套的,由於每個函數都有它自己的作用域,因此會出現幾個局部作用域嵌套的情況:
var scope = "global scope";//全局變量 function checkscope(){ var scope = "local scope";//局部變量 function nested(){ var scope = "nested scope";//嵌套作用域內的局部變量 return scope; //返回當前作用域內的值 } return scope+","+nested(); } console.log(scope+"|"+checkscope()); //==>global scope | local scope , nested scope
3.10.1 函數作用域和聲明提前:
在JavaScript中,沒有塊級作用域的概念,取而代之的是函數作用域(funciton scope):變量在聲明它們的函數體以及這個函數體嵌套的任意函數體內都是有定義的。JavaScript的函數作用域是指在函數內聲明的所有變量在函數體內始終是可見的——這意味着變量在聲明之前甚至已經可用。JavaScript的這個特性被非正式地稱爲聲明提前(hoisting),即JavaScript函數裏聲明的所有變量(但不涉及賦值)都被提前到函數體的頂部。
var scope = "global";//全局變量 (function f(){ console.log(scope); //==>"global" }());
再對比一下:
var scope = "global";//全局變量 (function f(){ console.log(scope); //==>undefined var scope = "local"; console.log(scope); //==>"local" }());
爲什麼呢?——因爲,局部變量scope的聲明被提前了,但是賦值沒有被提前,所以,以上代碼相當於:
var scope = "global";//全局變量 (function f(){ var scope; //聲明局部變量scope,此時沒有賦值 console.log(scope); //==>undefined scope = "local"; //給局部變量scope賦值 console.log(scope); //==>"local" }());
3.10.2 作爲屬性的變量
當聲明一個JavaScript全局變量時,實際上定義了全局對象的一個屬性,當使用var聲明一個變量時,創建的這個屬性是不可配置的,也就是說這個變量無法通過delete運算符刪除。但有一種特殊情況:如果沒有使用嚴格模式並給一個未聲明的變量賦值的話,JavaScript會自動創建一個全局變量,以這種方式創建的變量是全局對象的可配置值屬性並可以刪除它們:
var truevar = 1; //聲明一個不可刪除的全局變量 fakevar = 2;//創建全局對象的一個可刪除的屬性 this.fakevar2 = 3;//同上 console.log( delete truevar);//==>false console.log( delete fakevar);//==>true console.log( delete fakevar2);//==>true