大家好蚀同,我是IT修真院成都分院第10期學(xué)員李勁宏缅刽,一枚正直純潔善良的web程序員,今天給大家分享一下JS作用域蠢络。
任何程序設(shè)計語言都有作用域的概念衰猛,簡單的說,作用域就是變量與函數(shù)的可訪問范圍刹孔,即作用域控制著變量與函數(shù)的可見性和生命周期啡省。在JavaScript中,變量的作用域有全局作用域和局部作用域兩種髓霞。
1.全局作用域(Global Scope)
在代碼中任何地方都能訪問到的對象擁有全局作用域卦睹,一般來說一下幾種情形擁有全局作用域:
var aName="123";
function doSomething(){
? ? var bName="456";
}
console.log(aName); //123
console.log(bName); //腳本錯誤
(2)所有末定義直接賦值的變量自動聲明為擁有全局作用域,例如:
function a1(){
? ? num = 5;
}
a1();
console.log(num); //? 5
3)所有window對象的屬性擁有全局作用域
一般情況下方库,window對象的內(nèi)置屬性都都擁有全局作用域分预,例如window.name、window.top等等薪捍。
1. 局部作用域(Local Scope)
和全局作用域相反,局部作用域一般只在固定的代碼片段內(nèi)可訪問到配喳,最常見的例如函數(shù)內(nèi)部酪穿,所有在一些地方也會看到有人把這種作用域成為函數(shù)作用域。
例如下列代碼中的bName和函數(shù)innerSay都只擁有局部作用域:
function doSomething(){
? ? var bName="李云龍";
? ? function innerSay(){
? ? ? ? alert(bName);
? ? }
? ? innerSay();
}
alert(bName); //腳本錯誤innerSay(); //腳本錯誤
閉包
如何從外部讀取局部變量晴裹?? ? ? ? ? ? 出于種種原因被济,我們有時候需要得到函數(shù)內(nèi)的局部變量。但是涧团,前面已經(jīng)說過了只磷,正常情況下,這是辦不到的泌绣,只有通過變通方法才能實現(xiàn)钮追。? ? ? ? ? ? 那就是在函數(shù)的內(nèi)部,再定義一個函數(shù)阿迈。
function f1(){
? ? ? ? ? ? ? ? var n=999;
? ? ? ? ? ? ? ? function f2(){
? ? ? ? ? ? ? ? alert(n); // 999? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
在上面的代碼中元媚,函數(shù)f2就被包括在函數(shù)f1內(nèi)部,這時f1內(nèi)部的所有局部變量苗沧,對f2都是可見的刊棕。但是反過來就不行,f2內(nèi)部的局部變量待逞,對f1就是不可見的甥角。這就是Javascript語言特有的"鏈?zhǔn)阶饔糜?結(jié)構(gòu)(chain scope),子對象會一級一級地向上尋找所有父對象的變量识樱。所以嗤无,父對象的所有變量震束,對子對象都是可見的,反之則不成立翁巍。
? ? ? ? ? ? 既然f2可以讀取f1中的局部變量驴一,那么只要把f2作為返回值,我們不就可以在f1外部讀取它的內(nèi)部變量了嗎灶壶! function f1(){
? ? ? ? ? ? ? ? var n=999;
? ? ? ? ? ? ? ? function f2(){
? ? ? ? ? ? ? ? alert(n);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? return f2;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? var result=f1();
? ? ? ? ? ? ? ? result(); // 999? ? ? ? ? ?
閉包就是能夠讀取其他函數(shù)內(nèi)部變量的函數(shù)肝断。 由于在Javascript語言中,只有函數(shù)內(nèi)部的子函數(shù)才能讀取局部變量驰凛,因此可以把閉包簡單理解成"定義在一個函數(shù)內(nèi)部的函數(shù)"胸懈。 所以,在本質(zhì)上恰响,閉包就是將函數(shù)內(nèi)部和函數(shù)外部連接起來的一座橋梁趣钱。
閉包的注意點
1)由于閉包會使得函數(shù)中的變量都被保存在內(nèi)存中,內(nèi)存消耗很大胚宦,所以不能濫用閉包首有,否則會造成網(wǎng)頁的性能問題,在IE中可能導(dǎo)致內(nèi)存泄露枢劝。解決方法是井联,在退出函數(shù)之前,將不使用的局部變量全部刪除您旁。? ? ? ? ? ? 2)閉包會在父函數(shù)外部烙常,改變父函數(shù)內(nèi)部變量的值。所以鹤盒,如果你把父函數(shù)當(dāng)作對象(object)使用蚕脏,把閉包當(dāng)作它的公用方法(Public Method),把內(nèi)部變量當(dāng)作它的私有屬性(private value)侦锯,這時一定要小心驼鞭,不要隨便改變父函數(shù)內(nèi)部變量的值。
小課堂提問:
1.閉包的使用場景尺碰。?
<1>通過循環(huán)給頁面上多個DOM節(jié)點綁定時間终议,在JS中可以采用“立即執(zhí)行函數(shù)”的方式創(chuàng)建作用域。
<2>封裝變量葱蝗,閉包可以將一些不希望暴露在全局的變量封裝成私有變量穴张。
<3>延續(xù)局部變量的壽命。
什么是“自執(zhí)行函數(shù)”两曼?
我們先來說一下這個函數(shù)在叫法上的爭議皂甘。實際上不同的人對自執(zhí)行函數(shù)的理解不太一樣,第一種理解是悼凑,自執(zhí)行即自動執(zhí)行偿枕,也就是大家平時所謂的立即執(zhí)行函數(shù)璧瞬。
還有一種理解,即自執(zhí)行函數(shù)是在函數(shù)內(nèi)部執(zhí)行函數(shù)本身渐夸,即我們平時常說的遞歸函數(shù)嗤锉。不管是哪種理解,我們都沒必要去糾結(jié)叫法上的準(zhǔn)確與否墓塌,因為相比于自執(zhí)行函數(shù)瘟忱,立即執(zhí)行函數(shù)和遞歸函數(shù)這兩種叫法是我們更加熟悉的,實際上平時也很少會看到自執(zhí)行函數(shù)這種叫法苫幢。立即執(zhí)行函數(shù)和遞歸函數(shù)都很重要访诱。
閉包的優(yōu)點和缺點:
優(yōu)點:?
1.保護(hù)函數(shù)內(nèi)的變量安全,加強了封裝性?
2.在內(nèi)存中維持一個變量(用的太多就變成了缺點,占內(nèi)存)?
閉包之所以會占用資源是當(dāng)函數(shù)a執(zhí)行結(jié)束后, 變量i不會因為函數(shù)a的結(jié)束而銷毀, 因為b的執(zhí)行需要依賴a中的變量韩肝。
不適合場景:?
返回閉包的函數(shù)是個非常大的函數(shù)触菜。
閉包的典型框架應(yīng)該就是jquery了。?
閉包是javascript語言的一大特點哀峻,主要應(yīng)用閉包場合主要是為了:設(shè)計私有的方法和變量涡相。?
這在做框架的時候體現(xiàn)更明顯,有些方法和屬性只是運算邏輯過程中的使用的剩蟀,不想讓外部修改這些屬性漾峡,因此就可以設(shè)計一個閉包來只提供方法獲取。?
閉包的缺點就是常駐內(nèi)存喻旷,會增大內(nèi)存使用量,使用不當(dāng)很容易造成內(nèi)存泄露牢屋。
邏輯連續(xù)且预,當(dāng)閉包作為另一個函數(shù)調(diào)用的參數(shù)時,避免你脫離當(dāng)前邏輯而單獨編寫額外邏輯烙无。
方便調(diào)用上下文的局部變量锋谐。
加強封裝性,第2點的延伸截酷,可以達(dá)到對變量的保護(hù)作用涮拗。
缺點:
閉包有一個非常嚴(yán)重的問題,那就是內(nèi)存浪費問題迂苛,這個內(nèi)存浪費不僅僅因為它常駐內(nèi)存三热,更重要的是,對閉包的使用不當(dāng)會造成無效內(nèi)存的產(chǎn)生三幻。