作用域
作用域就是變量和函數(shù)的可訪問范圍,控制著變量和函數(shù)的可見性與生命周期茁帽,在JavaScript中變量的作用域有全局作用域和局部作用域。
單純的JavaScript作用域還是很好理解的敏簿,JavaScript沒有塊級的作用域媳搪,只有函數(shù)級作用域:變量在聲明它們的函數(shù)體及其子函數(shù)內(nèi)是可見的。
變量沒有在函數(shù)內(nèi)聲明或者聲明的時候沒有帶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函數(shù)里的所有聲明都被提前到函數(shù)體的頂部,而變量賦值操作留在原來的位置逊笆。
console.log(a);
var a = 1;
console.log(b);
實際上代碼如下:
var a; //變量聲明提升到函數(shù)頂部
console.log(a);
a= 1; //變量初始化依然保留在原來的位置
console.log(b);
作用域鏈
作用域鏈?zhǔn)亲饔糜蛞?guī)則的實現(xiàn)栈戳,通過作用域鏈的實現(xiàn),變量在它的作用域內(nèi)可被訪問难裆,函數(shù)在它的作用域內(nèi)可被調(diào)用子檀。
作用域鏈?zhǔn)且粋€只能單向訪問的鏈表,這個鏈表上的每個節(jié)點就是執(zhí)行上下文的變量對象(代碼執(zhí)行時就是活動對象)差牛,單向鏈表的頭部(可被第一個訪問的節(jié)點)始終都是當(dāng)前正在被調(diào)用執(zhí)行的函數(shù)的變量對象(活動對象)命锄,尾部始終是全局活動對象。
var a = 1
function fn1(){
function fn2(){
console.log(a)
}
function fn3(){
var a = 4
fn2()
}
var a = 2
return fn3
}
var fn = fn1()
fn() //2
在函數(shù)運行過程中標(biāo)識符的解析是沿著作用域鏈一級一級搜索的過程偏化,從第一個對象開始脐恩,逐級向后回溯,直到找到同名標(biāo)識符為止侦讨,找到后不再繼續(xù)遍歷驶冒,找不到就報錯苟翻。
閉包
函數(shù)對象可以通過作用域鏈相互關(guān)聯(lián)起來,函數(shù)體內(nèi)的數(shù)據(jù)(變量和函數(shù)聲明)都可以保存在函數(shù)作用域內(nèi)骗污,這種特性在計算機(jī)科學(xué)文獻(xiàn)中被稱為“閉包”崇猫。既函數(shù)體內(nèi)的數(shù)據(jù)被隱藏于作用于鏈內(nèi),看起來像是函數(shù)將數(shù)據(jù)“包裹”了起來需忿。從技術(shù)角度來說诅炉,js的函數(shù)都是閉包:函數(shù)都是對象,都關(guān)聯(lián)到作用域鏈屋厘,函數(shù)內(nèi)數(shù)據(jù)都被保存在函 數(shù)作用域內(nèi)涕烧。
var count = (function() { //定義函數(shù)并立即調(diào)用
var counter = 0; //函數(shù)的自由狀態(tài)
return function() {return counter++;};
}());
這段代碼定義一個立即返回函數(shù),返回值賦值給變量count汗洒。這個函數(shù)返回另外一個函數(shù)议纯,也叫嵌套函數(shù),溢谤,既可以訪問作用域內(nèi)的變量又可以訪問外部函數(shù)變量counter瞻凤。當(dāng)外部函數(shù)返回之后,其他代碼無法訪問counter世杀,只有內(nèi)部函數(shù)可以訪問阀参。
其實理解了執(zhí)行環(huán)境和作用域鏈后,閉包翻了變成顯而易見的東西玫坛,但是也不能濫用閉包结笨,從上面例子可以看出,閉包會使子函數(shù)保持其作用域鏈的所有變量及函數(shù)與內(nèi)存中湿镀,內(nèi)存消耗很大炕吸,在使用的時候盡量銷毀父函數(shù)不再使用的變量。