本文也同步發表在我的公眾號“我的天空” 上一期我們已經介紹了閉包,由於閉包可以延長函數內部的變數的生存周期,因此我們可以將不需要暴露在全局的變數封裝成函數的內部變數,從而避免代碼污染。 譬如要實現一個簡單的累加器,為了保存每次累加的結果,因此聲明瞭一個全局變數total,代碼如下: var tota ...
本文也同步發表在我的公眾號“我的天空”
上一期我們已經介紹了閉包,由於閉包可以延長函數內部的變數的生存周期,因此我們可以將不需要暴露在全局的變數封裝成函數的內部變數,從而避免代碼污染。
譬如要實現一個簡單的累加器,為了保存每次累加的結果,因此聲明瞭一個全局變數total,代碼如下:
var total=0;
function add(t){
total+=t;
alert(total);
}
total=2;
add(3); //顯示5
add(5); //顯示10
add(1); //顯示11
但是在實際開發中,應儘量避免全局變數,因為全局變數可以在代碼的任何地方被調用,假設在其他地方,不小心更改了total的值的話,我們這個累加器就會出問題了。由於total值是只供函數add()使用的,因此希望total能被封閉在函數add()的內部,這樣,就無法從外部來改寫它了,我們使用閉包來實現,代碼如下:
function add(s){
var total=s;
return function(t){
total+=t;
alert(total);
}
}
var a=add(2);
a(3); //顯示5
a(5); //顯示10
a(1); //顯示11
通過閉包,將變數total封閉在函數add()中,外部無法訪問到,這樣就避免了被其他代碼隨意改寫的可能性。
由於閉包會將封閉在函數內部的局部變數賦予類似於全局變數的效果,因此在有些場景下需要特別註意,尤其是涉及到迴圈遍歷,來看以下代碼:
function createArray(){
var result=new Array();
for (var i=0;i<3;i++){
result[i]=function(){
return i;
}
}
}
這是一個創建數組數組的函數,從錶面上看,每個函數應該都返回自己的索引值,因此創建的數組中,每個元素應該包含如下函數:
result[0]:function(){return 0}
result[1]:function(){return 1}
result[2]:function(){return 2}
但是實際上,數組中的每個元素只是包含:function(){return i},也就是說,當該函數執行完畢後,返回的是這樣一個數組:
{
function(){return i},
function(){return i},
function(){return i}
}
而變數i由於存在於一個返回函數中,形成了閉包,所以當createArray()執行完畢後,其執行環境不會被銷毀,變數i得以保留,並且其值為3(這點很重要)。
因此,當我們使用createArray()來創建數組時,得到的效果就不是我們的預期,彈出的都為“3”:
var a=createArray();
for(var z=0;z<a.length;z++){
alert(a[z]()); //均顯示為3
}
實際上該代碼無非就是重覆執行三遍以下代碼:
alert(function(){return 3}());
那麼為了達到我們的預期,應該將createArray()函數做如下修改:
function createArray(){
var result=new Array();
for (var i=0;i<3;i++){
result[i]=function(z){
return function(){
return z;
};
}(i)
}
}
分析以上代碼,我們將一個自執行函數返回給了數組元素,在賦值的時候,變數z就是在賦值的那個時刻的i值,那麼返回的數組中的元素便包含我們預期的函數:
result[0]:function(){return 0}
result[1]:function(){return 1}
result[2]:function(){return 2}
再一次執行以下代碼,顯示就正常了:
var a=createArray();
for(var z=0;z<a.length;z++){
alert(a[z]()); //依次顯示0、1、2
}
一定要註意的是,我們是把一個函數賦予了數組中的元素,而不是單個的值。因為在實際的應用中,返回函數的話,我們就可以在函數內做更多的事情。
看以下的實現,html有四個p標簽和4個div標簽,當單擊div標簽時相應的p標簽更改顏色,請註意這是一個面試中非常容易遇到的題目,代碼如下:
<body>
<p>p1</p><p>p2</p><p>p3</p><p>p4</p>
<div>div1</div><div>div2</div><div>div3</div><div>div4</div>
</body>
<script>
var d=document.getElementsByTagName("div");
for(var i=0;i<d.length;i++){
d[i].onclick=function(num){
return function(){
document.getElementsByTagName("p")[num].style.color="red";
};
}(i);
}
</script>
如果直接寫成以下代碼的話,那麼無論你單擊哪個div,程式總是會報錯,因為此時i的值為4,所以document.getElementsByTagName("p")[4]這個元素並不存在,導致引用錯誤。
for(var i=0;i<d.length;i++){
d[i].onclick=function(){
document.getElementsByTagName("p")[i].style.color="red";
};
}
最後我們要註意的是當需要返回函數內部的多個變數時,便不能採用返回匿名函數的方式了,可以採用以下的形式:
function setpepole(){
var name="李四";
var age=31;
return {
getname:funcion(){
return name;
},
getage:function(){
return age;
}
}
}
var a=setpepole();
alert(a.getname()); //顯示“李四”
alert(a.getage()); //顯示31
閉包系列就到此全部結束了!