什麼是作用域? 作用域規定變數在什麼地方可用。 函數級作用域 1.函數外聲明的變數為全局變數,函數內可以直接訪問全局變數: 2.JavaScript變數的作用域是 函數級 的,只有函數可以產生新的作用域,而非塊級: 變數x雖然在塊語句(if)中聲明並賦值,但它的作用域是函數a,所以在函數a的任何位置 ...
什麼是作用域?
作用域規定變數在什麼地方可用。
函數級作用域
1.函數外聲明的變數為全局變數,函數內可以直接訪問全局變數:
var global_var = 10; //全局變數
function a(){
alert(global_var); //全局變數在函數內可訪問
}
a(); //10
2.JavaScript變數的作用域是函數級的,只有函數可以產生新的作用域,而非塊級:
function a(){ //函數
if(true){ //塊
var x = 1;
}
if(false){
var y = 2;
}
alert(x); //1
alert(y); //undefined
}
a();
變數x雖然在塊語句(if)中聲明並賦值,但它的作用域是函數a,所以在函數a的任何位置它都可訪問。
有意思的是y變數的聲明和賦值雖然在false塊語句里,卻仍然列印出undefined而不是報錯,因為JavaScript會把所有變數聲明提前到函數開頭,稱為變數提升:
function a(){
alert(x);
var x = 1;
alert(x);
}
a(); //undefined 1
//以上代碼經過JavaScript變數提升後實際上是這個樣子的:
function a(){
var x;
alert(x);
x = 1;
alert(x);
}
需要註意的是,在函數內聲明變數一定要使用var,否則是聲明瞭一個全局變數:
function test(){
a = 1;
}
test();
alert(a); //1
3.嵌套函數可以訪問外圍函數的變數
function a(){
var x = 1;
function b(){
alert(x);
var y = 2;
function c(){
alert(x);
alert(y);
}
c();
}
b();
}
a(); // 1 1 2
需要註意的是,嵌套函數里如果有同名變數,那麼訪問到的是嵌套函數里的變數
function a(){
var x = 1;
function b(){
var x = 2;
alert(x);
}
b();
alert(x);
}
a(); //2 1
如何做到塊級作用域
通過上面的例子我們瞭解到JavaScript變數作用域是函數級的,但有時候我們想用臨時變數怎麼辦呢?
通過IIFE(立即執行函數表達式)來實現:
function a(){
if(true){
(function(){ //IIFE開始
var x = 1;
alert(x); //1
}()); //IIFE結束
//alert(x); //這兒訪問不到
}
//alert(x); //這兒訪問不到
}
a();
這樣做的好處是不會造成變數污染,用完就沒了,大家商量好,過了今晚就不再聯繫。
作用域鏈
每當JavaScript解釋器進入一個函數,它都會看一下附近有哪些局部變數,把它們保存到該函數的variables對象中,並創建一個scope屬性來指向外部的variables對象
1. var x=1;
2. function a() {
3. var y = 2;
4. function b() {
5. var z = 3;
6. alert(x+y+z);
7. }
8. b();
9. }
10. a();
結合上面的代碼,看看JavaScript引擎是如何處理作用域的:
JavaScript把所有全局對象(變數x和函數a)放到global variables對象中
global variables: x, a()
發現a是個函數,它需要一個scope屬性來指向外部的variables(全局),同時把變數保存起來
a.scope -> global variables
a.variables: y, b()
來到第4行,發現b是個函數,它需要一個scope屬性來指向它所在的作用域(a)
b.scope -> a.variables
b.variables: z
查找變數的時候,先看看variables對象有沒有,沒有就根據scope找上一級的variables,就這樣一層一層往上找,直到找到為止。
參考資料
- JavaScript for PHP Developers
- 深入理解JavaScript
- You Don't Know JS