作用域
作用域就是變量和函數(shù)的可訪問范圍,控制著變量和函數(shù)的可見性與生命周期施掏,在JavaScript中變量的作用域有全局作用域和局部作用域本讥。
單純的JavaScript作用域還是很好理解的典唇,在一些類C編程語言中花括號內(nèi)的每一段代碼都有各自的作用域侯勉,而且變量在聲明它們的代碼段外是不可見的,稱之為塊級的作用域,JavaScript容易讓初學(xué)者誤會的地方也在于此拖刃,JavaScript并沒有塊及的作用域删壮,只有函數(shù)級作用域:變量在聲明它們的函數(shù)體及其子函數(shù)內(nèi)是可見的。
變量沒有在函數(shù)內(nèi)聲明或者聲明的時(shí)候沒有帶var就是全局變量兑牡,擁有全局作用域央碟,window對象的所有屬性擁有全局作用域;在代碼任何地方都可以訪問均函,函數(shù)內(nèi)部聲明并且以var修飾的變量就是局部變量亿虽,只能在函數(shù)體內(nèi)使用,函數(shù)的參數(shù)雖然沒有使用var但仍然是局部變量苞也。
代碼如下:
var a=3; //全局變量
function fn(b){ //局部變量
c=2; //全局變量
var d=5; //局部變量
function subFn(){
var e=d; //父函數(shù)的局部變量對子函數(shù)可見
for(var i=0;i<3;i++){
console.write(i);
}
alert(i);//3, 在for循環(huán)內(nèi)聲明洛勉,循環(huán)外function內(nèi)仍然可見,沒有塊作用域
}
}
alert(c); //在function內(nèi)聲明但不帶var修飾如迟,仍然是全局變量
只要是理解了JavaScript沒有塊作用域收毫,簡單的JavaScript作用域很好理解,還有一點(diǎn)兒容易讓初學(xué)者迷惑的地方是JavaScript變量可函數(shù)的與解析或者聲明提前殷勘,好多種叫法但說的是一件事情此再,JavaScript雖然是解釋執(zhí)行,但也不是按部就班逐句解釋執(zhí)行的玲销,在真正解釋執(zhí)行之前输拇,JavaScript解釋器會預(yù)解析代碼,將變量贤斜、函數(shù)聲明部分提前解釋策吠,這就意味著我們可以在function聲明語句之前調(diào)用function,這多數(shù)人習(xí)以為常瘩绒,但是對于變量的與解析乍一看會很奇怪
只要是理解了JavaScript沒有塊作用域奴曙,簡單的JavaScript作用域很好理解,還有一點(diǎn)兒容易讓初學(xué)者迷惑的地方是JavaScript變量可函數(shù)的與解析或者聲明提前草讶,好多種叫法但說的是一件事情,JavaScript雖然是解釋執(zhí)行炉菲,但也不是按部就班逐句解釋執(zhí)行的堕战,在真正解釋執(zhí)行之前,JavaScript解釋器會預(yù)解析代碼拍霜,將變量嘱丢、函數(shù)聲明部分提前解釋,這就意味著我們可以在function聲明語句之前調(diào)用function祠饺,這多數(shù)人習(xí)以為常越驻,但是對于變量的與解析乍一看會很奇怪.
代碼如下:
console.log(a); //undefined
var a=3;
console.log(a); //3
console.log(b); //Uncaught ReferenceError: b is not defined
上面代碼在執(zhí)行前var a=3; 的聲明部分就已經(jīng)得到預(yù)解析(但是不會執(zhí)行賦值語句),所以第一次的時(shí)候會是undefined而不會報(bào)錯(cuò),執(zhí)行過賦值語句后會得到3缀旁,上段代碼去掉最后一句和下面代碼是一樣的效果记劈。
代碼如下:
var a;
console.log(a); //undefined
a=3;
console.log(a); //3
然而
如果只是這樣那么JavaScript作用域問題就很簡單了,然而由于函數(shù)子函數(shù)導(dǎo)致的問題使作用域不止這樣簡單并巍。大人物登場——執(zhí)行環(huán)境或者說運(yùn)行期上下文(好土鱉):執(zhí)行環(huán)境(execution context)定義了變量或函數(shù)有權(quán)訪問的其它數(shù)據(jù)目木,決定了它們的各自行為。每個(gè)執(zhí)行環(huán)境都有一個(gè)與之關(guān)聯(lián)的變量對象(variable object, VO)懊渡,執(zhí)行環(huán)境中定義的所有變量和函數(shù)都會保存在這個(gè)對象中刽射,解析器在處理數(shù)據(jù)的時(shí)候就會訪問這個(gè)內(nèi)部對象。
全局執(zhí)行環(huán)境是最外層的一個(gè)執(zhí)行環(huán)境剃执,在web瀏覽器中全局執(zhí)行環(huán)境是window對象誓禁,因此所有全局變量和函數(shù)都是作為window對象的屬性和放大創(chuàng)建的。每個(gè)函數(shù)都有自己的執(zhí)行環(huán)境肾档,當(dāng)執(zhí)行流進(jìn)入一個(gè)函數(shù)的時(shí)候摹恰,函數(shù)的環(huán)境會被推入一個(gè)函數(shù)棧中,而在函數(shù)執(zhí)行完畢后執(zhí)行環(huán)境出棧并被銷毀阁最,保存在其中的所有變量和函數(shù)定義隨之銷毀戒祠,控制權(quán)返回到之前的執(zhí)行環(huán)境中,全局的執(zhí)行環(huán)境在應(yīng)用程序退出(瀏覽器關(guān)閉)才會被銷毀速种。
作用域鏈
當(dāng)代碼在一個(gè)環(huán)境中執(zhí)行時(shí)姜盈,會創(chuàng)建變量對象的一個(gè)作用域鏈(scope chain,不簡稱sc)來保證對執(zhí)行環(huán)境有權(quán)訪問的變量和函數(shù)的有序訪問。作用域第一個(gè)對象始終是當(dāng)前執(zhí)行代碼所在環(huán)境的變量對象(VO)
代碼如下:
function a(x,y){
var b=x+y;
return b;
}
在函數(shù)a創(chuàng)建的時(shí)候它的作用域鏈填入全局對象,全局對象中有所有全局變量
如果執(zhí)行環(huán)境是函數(shù)配阵,那么將其活動(dòng)對象(activation object, AO)作為作用域鏈第一個(gè)對象馏颂,第二個(gè)對象是包含環(huán)境,下一個(gè)是包含環(huán)境的包含環(huán)境棋傍。救拉。。瘫拣。衣迷。
代碼如下:
function a(x,y){
var b=x+y;
return b;
}
var tatal=a(5,10);
在函數(shù)運(yùn)行過程中標(biāo)識符的解析是沿著作用域鏈一級一級搜索的過程,從第一個(gè)對象開始晨抡,逐級向后回溯特笋,直到找到同名標(biāo)識符為止,找到后不再繼續(xù)遍歷拢切,找不到就報(bào)錯(cuò)蒂萎。