JavaScript中有以下兩種作用域
全局作用域
函數(shù)作用域
全局作用域是函數(shù)之外(最外層代碼)的作用域钓觉。在函數(shù)之外進(jìn)行聲明的名稱屬于全局作用域。這些名稱就是所謂的全局變量以及全局函數(shù)于样。
而在函數(shù)內(nèi)部進(jìn)行聲明的名稱擁有的是函數(shù)作用域,它們僅在函數(shù)內(nèi)部才有效做院。相對(duì)于全局作用域沉填,可以稱之為局部作用域市框;相對(duì)于全局變量可以將其成為局部變量霞扬。作為函數(shù)形參的參數(shù)變量也屬于函數(shù)作用域。
var x = 1;
function f() {
alert("x:"+x);
var x = 2;
alert("x:"+x);
};
f(); // undefined | 2
可能會(huì)認(rèn)為函數(shù)f內(nèi)第一個(gè)alert顯示的是全局變量x枫振。然而喻圃,這里的x是在下一行進(jìn)行聲明的局部變量x。這是因?yàn)榉嗦耍植孔兞縳的作用域是整個(gè)函數(shù)f內(nèi)部斧拍。由于此時(shí)還沒有對(duì)其進(jìn)行賦值,因此變量x的值為undefined杖小。
等價(jià)于:
function f() {
var x;
alert("x:"+x);
x = 2;
alert("x:"+x);
};
f();
所以肆汹,未避免發(fā)生錯(cuò)誤愚墓,一般建議在函數(shù)開始處對(duì)所有的局部變量進(jìn)行聲明。
塊級(jí)作用域
var x = 1;
{var x = 2;alert(x);} // 2
alert(x); // 2
如果認(rèn)為塊級(jí)作用域存在昂勉,則會(huì)認(rèn)為第二個(gè)alert的結(jié)果會(huì)是1浪册,可是結(jié)果是2
看似是在代碼塊中重新聲明了塊級(jí)作用域中的變量x,但實(shí)際上硼啤,它只是將全局變量賦值為了2议经。等價(jià)于:
var x = 1;
{x:2;alert(x);} // 2
x = 2;
alert(x); // 2
在函數(shù)作用域中也存在對(duì)塊級(jí)作用域的誤解斧账。在for語句中對(duì)循環(huán)變量進(jìn)行聲明是一種習(xí)慣做法谴返,不過該循環(huán)變量的作用域并不局限與for語句內(nèi)。下面的代碼中咧织,其實(shí)是對(duì)局部變量i進(jìn)行了循環(huán)使用嗓袱。
function f() {
var i = 1;
for (var i=1;i<10;i++) {
// 省略
}
// 此時(shí)變量i的值為10
};
let與塊級(jí)作用域
ECMAScript5中沒有塊級(jí)作用域,但是JavaScript自帶有l(wèi)et這一增強(qiáng)功能习绢。感興趣的可以試一試
嵌套函數(shù)與作用域
在JavaScript中可以對(duì)函數(shù)進(jìn)行嵌套聲明渠抹。就是說,在一個(gè)函數(shù)中聲明另一個(gè)函數(shù)闪萄。這時(shí)梧却,內(nèi)部函數(shù)可以訪問外部函數(shù)的作用域。從形式上來說败去,名稱的查找是由內(nèi)向外的放航。在最后會(huì)查找全局作用域中的名稱。
function f1() {
var x = 1; // 函數(shù)f1的局部變量
// 嵌套函數(shù)聲明
function f2() {
var y = 2; // 函數(shù)f2的局部變量
alert(x); // 將向上一層訪問f1的局部變量
alert(y);
};
function f3() {
alert(y); // 如果不存在全部變量y圆裕,將返回ReferenceError
}
// 調(diào)用
f2();
f3();
};
f1();
變量隱藏
指的是广鳍,通過作用域較小的變量(或者函數(shù)),來隱藏作用域較大的同名變量(或函數(shù))吓妆。這種情況常常會(huì)在無意中發(fā)生赊时,從而造成錯(cuò)誤。如:
var n = 1;
function f() {
var n = 2;
alert(n);
};
f();
// 這里就是局部變量隱藏了全局變量
接下來做題做題:
var a = 10;
function test(){
a = 100;
console.log(a);
console.log(this.a);
var a;
console.log(a);
}
test();
在JS腳本執(zhí)行前行拢,JS解釋器會(huì)對(duì)整個(gè)腳本聲明部分做解析祖秒,從而確定變量作用域,這里的var a會(huì)因?yàn)樽兞柯暶魈嵘鄣欤瑫?huì)被首先執(zhí)行狈涮,然后才是賦值(賦值的時(shí)間嚴(yán)格按照程序執(zhí)行順序來進(jìn)行),第一個(gè)console輸出100鸭栖;
第二個(gè)console我們發(fā)現(xiàn)了this引用歌馍,看見this引用就必須想this被誰調(diào)用,test函數(shù)是全局函數(shù)晕鹊,當(dāng)然是全局對(duì)象調(diào)用松却,所以就等于window.a暴浦,而全局變量已經(jīng)在開頭聲明過了var a=10,輸出10晓锻;
最后一個(gè)console歌焦,這個(gè)a是誰?因?yàn)樘幱诤瘮?shù)作用域內(nèi)砚哆,查找由內(nèi)至外独撇,局部變量已經(jīng)存在,當(dāng)然輸出局部變量躁锁,結(jié)果為100纷铣;
100/10/100
var a = 100;
function test(){
console.log(a);
var a = 10;
console.log(a);
}
test()
分析函數(shù),變量聲明提前战转,所以var a = 10會(huì)在函數(shù)執(zhí)行前先進(jìn)行聲明搜立,但是未被賦值!賦值的時(shí)間按照源碼的順序照常執(zhí)行槐秧。所以第一個(gè)console是undefined啄踊;
然后進(jìn)行賦值a = 10,之后再console刁标,結(jié)果10颠通。
var a = 100;
function test(){
console.log(a);
a = 10;
console.log(a);
}
test();
console.log(a);
這里test函數(shù)內(nèi)部有聲明var嗎?沒有膀懈,所以這里的a其實(shí)是全局變量中的a顿锰,則第一個(gè)console結(jié)果為100;
之后賦值覆蓋了原來a變量的值,console結(jié)果10吏砂;
最后在函數(shù)外部的console撵儿,顯然的這里全局變量已經(jīng)被重新賦值,所以console結(jié)果為10狐血;
100/10/10
最后來一題:
var foo = 1;
function main(){
alert(foo);
var foo = 2;
alert(this.foo)
this.foo = 3;
};
// 請(qǐng)給出以下兩種方式調(diào)用函數(shù)時(shí)淀歇,alert的結(jié)果,并說明原因匈织。
var m1 = main(); // undefined | 1
var m2 = new main(); // undefined | undefined
首先是m1浪默,這里的main函數(shù)是普通調(diào)用,則按照普通函數(shù)處理缀匕。內(nèi)部var foo = 2會(huì)被提前聲明纳决,所以第一個(gè)alert彈出undefined,之后賦值2乡小;
第二個(gè)alert調(diào)用的是this.foo,this引用此時(shí)指向window全局對(duì)象阔加,所以彈出的是全局變量foo的值1;
最后將全局變量重新賦值為3;
但是m1的值是什么满钟?是undefined胜榔。這個(gè)函數(shù)沒有指定return胳喷,默認(rèn)返回undefined。
在來看m2夭织,這里是構(gòu)造函數(shù)調(diào)用吭露,當(dāng)時(shí)函數(shù)內(nèi)部該執(zhí)行的會(huì)繼續(xù)執(zhí)行,但是我們肯定知道的是構(gòu)造函數(shù)的實(shí)例對(duì)象的引用會(huì)賦值給m2尊惰;
第一個(gè)alert不變讲竿,值為undefined,之后局部變量foo被賦值為2弄屡;
運(yùn)行到第二個(gè)alert题禀,這里的this引用指向誰?指向m2琢岩。等價(jià)于m2.foo投剥,但是此時(shí)的m2未被定義(在下一行代碼)师脂。所以alert彈出undefined担孔。