標籤:style blog io ar color 使用 sp java for
1.變數的範圍:全域變數、局部變數
js代碼
var n=2;
function fun(){
alert(n);
}
fun(); //2
js代碼
function fun(){
var n=2; //注意這裡要使用var,否則聲明的是全域變數
}
alert(n); //error
js代碼
function fun(){
n=2;}
alert(n); //2
2.如何從外部讀取局部變數?
有時候需要得到函數內部的局部局部變數,正常情況下是辦不到的,只有變通才有可能。在函數內部在定義一個函數
js代碼
function fun(){
var n=2;
function f2(){
alert(n); //2
}
}
上面代碼,f2函數包含在fun函數中,這時fun內部的局部變數對f2是可見的,而f2內部局部變數,對fun不可見,這就是javascript"鏈式範圍"結構
子物件會一級一級向上尋找所有父物件的變數,即父物件的所有變數對子物件都可見,反之則不成立
js代碼
function fun(){
var n=2;
function f2(){
alert(n);
}
return f2;
}
var result=fun();
result(); //2
3.閉包概念
上面f2函數就是閉包
閉包簡單理解就是:能夠讀取其他函數內部變數的函數,即將函數內部與函數外部串連起來的橋樑
4.閉包的用途
事實上,通過使用閉包,可以做很多事情。比如:類比物件導向的代碼風格;更優雅,更簡潔地表達出代碼;在某些方面提升代碼的執行效率
註:根據上面知識可知,所有的變數如果不加關鍵字,則預設會添加到全域對象的屬性上,臨時變數加入全域變數有很多壞處,如:別的函數可能誤用這些變數,造成全域對象過於龐大,影響訪問速度(因為變數的取值需要從原型鏈上遍曆)。
4.1匿名子執行函數
有些函數只需執行一次,其內部變數無序維護,比如UI的初始化,我們可以使用閉包
js代碼
var datamodel={ table:[]; tree:[];}(function(dm){for(var i=0;i<dm.table.rows;i++){ var row=dm.table.rows[i]; for(var j=0;j<row.cells;i++){ drawCell(i,j); } } //build dm.tree})(datamodel);
建立一個匿名函數並立即執行它,由於外部無法引用它內部的變數,因此在執行完後會很快被釋放,最主要的是這種機制不會汙染全域變數
4.2 緩衝
如果我們正在處理過程很耗時的函數對象,每次調用都會花費很長時間,那麼我們就需要將計算出來的值儲存起來,當調用這個函數的時候,首先在緩衝中尋找,如果找不到,則進行計算,然後更新緩衝並傳回值,如果找到了,直接返回尋找的值即可。
閉包正是可以做到這一點,因為它不會釋放外部的引用,函數內部的值可以得到保留。減少全域變數,常駐記憶體,可以實現緩衝
js代碼
function f1(){ var n=99; nAdd=function(){ n+=1; } function f2(){ alert(n); } return f2; } var result=f1(); result();//99 nAdd(); result();//100
為什麼f1中的局部變數n一直儲存在記憶體中,沒有在f1調用結束後自動清除?
f1是f2的父函數,f2被賦給全域變數,導致f2始終在記憶體中,而f2的存在依賴f1,因此f1也始終在記憶體中,不會再調用結束後,被記憶體回收機制回收
4.3 實現封裝
4.3.1如下例子,在person之外的地方無法訪問其內部的變數,通過提供閉包的形式來訪問
js代碼
var person=function(){ var name="jack"; return{ getName:function(){ return name; }, setName:function(newName){ name=newName; } } }(); alert(person.name); //直接存取結果為:undefined alert(person.getName()); //jack person.setName("lihua"); alert(person.getName()); //lihua
4.3.2
閉包另一個重要用途是實現物件導向中的對象,傳統的語言都提供類的模板機制,這樣不同的對象(類的執行個體)擁有獨立的成員及狀態,互不干涉。雖然javascript中沒有類這樣的機制,但通過使用閉包,可以類比出這樣的機制。
js代碼
function person(){ var name="jack"; return{ getName:function(){ return name; }, setName:function(newName){ name=newName; } } }; var jone=person(); alert(jone.getName()); //jack jone.setName("lihua"); alert(jone.getName()); //lihua var miss=person(); alert(miss.getName()); miss.setName("wanghai"); alert(miss.getName());
以上代碼中Jone和miss可以成為person這個類的執行個體,因為這兩個執行個體對name這個成員的訪問時獨立的,互補影響。
5使用閉包應該注意問題
5.1記憶體泄露
閉包會使函數中的變數都儲存在記憶體中,記憶體消耗大,故不能濫用閉包,否則造成網頁效能問題(瀏覽器響應速度慢、降低使用者體驗甚至會造成瀏覽器無響應等現象),在IE中可能導致記憶體泄露
5.2上下文應用
jQuery代碼
$(function(){
var con=$("div#panel");
this.id="content";
con.click(function(){
alert(this.id); //panel
});
});
this.id 顯示的被賦值content,而在click回調中,形成的閉包引用到this.id,但這個alert會彈出"panel",原因:雖然閉包可以引用局部變數,但此處涉及到this,因為調用對象的存在,使得閉包被調用時(當這個panel的click事件發生時),此處的this引用的是con這個jQuery對象,而匿名函數this.id="content"是對匿名函數本身做的操作。
如果想在事件中處理函數中訪問的這個值,則必須做些改變
$(function(){
var con=$("div#panel");
this.id="content";
var self=this;
con.click(function(){
alert(self.id); //content
});
});
初探js閉包