首先說一下閉包的概念,**閉包**是指有權訪問另一個函數作用域中的變量的函數。創建閉包的常見方式,就是在一個函數內部創建另一個函數。
先從簡單的例子開始:
~~~
function comparison(propertyName){
return function(object1, object2){
var value1 = object1[propertyName];
var value2 = object2[propertyName];
if(value1 < value2){
return -1;
}else if(value1 > value2){
return 1;
}else{
return 0;
}
};
}
~~~
上面的函數comparison()中返回的匿名函數就是一個閉包,在這個匿名函數中,訪問了外部函數中的變量propertyName,這個內部函數被返回之后,在其它地方調用時,仍然可以訪問變量propertyName,原因是因為內部函數的作用域鏈包含外部函數comparison()的作用域。
~~~
var compare = comparison("age");
var result = compare({"age": 25},{"age": 35});
console.log(result); //25, 35
~~~
在comparison()函數被調用時,會新建一個包含arguments和propertyName的活動對象,var compare = comparison(“age”);返回匿名函數,匿名函數的作用域鏈被初始化為包含comparison()函數的活動對象和全局變量對象。在comparison()函數執行完畢后,其執行環境的作用鏈會被銷毀,但是活動對象不會,因為匿名函數的作用域鏈要引用這個活動對象。
compare返回的是內部的匿名函數,即:下面的這個函數
~~~
function(object1, object2){
var value1 = object1[propertyName];
var value2 = object2[propertyName];
if(value1 < value2){
return -1;
}else if(value1 > value2){
return 1;
}else{
return 0;
}
};
~~~
在第二部,var result = compare({“age”: 25},{“age”: 35})時,傳入了object1和object2,但是并沒有傳入propertyName,但是函數仍然可以返回正確的結果,就是因為這時訪問的是外部函數comparison()的活動對象propertyName的值”age”。外部函數的活動對象不會被銷毀,除非你們函數被銷毀。
如果我們在最后加上一句:
~~~
compare = null;
~~~
這就解除了對匿名函數的引用。垃圾回收例程才會將其清除,否則其將一直存在于內存之中,因為垃圾回收例程不知道何時它還會被再使用。由于閉包會攜帶包含它的函數的作用域,因此會比其它函數占用更多的內存,因此要避免過度使用閉包。
~~~
function newFunctions(){
var result = new Array();
for(var i = 0; i < 10; i++){
result[i] = function(){
return i;
}
}
return result;
}
var values = newFunctions();//返回長度為10的函數數組
console.log(values[2]()); //10
console.log(values[5]()); //10
~~~
可以看出,`values[2]()`和`values[5]()`并沒有像我們料想中的那樣返回2,和5,而是全部返回的是10,這是因為,每個函數的作用域中都保存著newFunctions()函數的活動對象,因此它們引用的是同一個變量i。當newFunctions()返回時,變量i的值都是10。此時,每個函數引用的都是保存變量i的同一個變量對象,因此每個函數內部的i的值都是10.
可以通過創建一個匿名函數強制讓閉包的行為符合預期。
~~~
function newFunctions(){
var result = new Array();
for(var i = 0; i < 10; i++){
result[i] = function(num){
return function(){
return num;
}
}(i);
}
return result;
}
var values = newFunctions();//返回長度為10的函數數組
console.log(values[2]()); //2
console.log(values[5]()); //5
~~~
定義一個匿名函數,并將立即執行該匿名函數的結果賦值給數組。匿名函數有一個參數num,也是函數最終要返回的值。在每次調用這個函數時,都傳入了一個變量i,每次將i的當前值復制給參數num。在這個匿名函數的內部,又創建并返回num的閉包。這樣,result數組中的每個函數都有自己num變量,而不是像之前一樣,全部都引用同樣的一個i變量,因此就可以返回各自不同的數值了。
~~~
function(num){
return function(){
return num;
}
}(i);
~~~
閉包是JS中一個比較重要的概念,對于初學者來說,可能也是較為難以理解的知識點,希望本文來帶給大家一點幫助。
- 前言
- jQuery輪播圖插件
- JS模擬事件操作
- JS閉包與變量
- JS綁定事件
- HTML5之file控件
- JavaScript的this詞法
- JavaScript的this詞法(二)
- JS this詞法(三)
- JS檢測瀏覽器插件
- JS拖拽組件開發
- number輸入框
- Modernizr.js和yepnode.js
- DOM變化后事件綁定失效
- div和img之間的縫隙問題
- 詳解JavaScript作用域
- bootstrap入門
- 表單驗證(登錄/注冊)
- Bootstrap網格系統
- Bootstrap排版
- Bootstrap創建表單(一)
- Bootstrap表單(二)
- Bootstrap按鈕
- Bootstrap圖片
- Bootstrap字體圖標(glyphicons)
- Bootstrap的aria-label和aria-labelledby
- Bootstrap下拉菜單
- Bootstrap按鈕組
- Bootstrap按鈕菜單
- Bootstrap輸入框組
- Bootstrap導航元素
- Bootstrap導航欄
- sublimeText頻頻崩潰
- JQuery不同版本的差異(checkbox)
- Bootstrap面包屑導航、分頁、標簽、徽章
- Bootstrap警告
- Bootstrap進度條
- 前端的上傳下載
- JS字符串的相關方法
- CSS3選擇器(全)
- CSS3新增文本屬性詳述
- 利用CSS3實現圖片切換特效
- CSS3新增顏色屬性
- CSS3的border-radius屬性詳解
- JS創建對象幾種不同方法詳解
- JS實現繼承的幾種方式詳述(推薦)
- CSS3響應式布局
- JS模塊化開發(requireJS)
- 利用@font-face實現個性化字體
- 前端在html頁面之間傳遞參數的方法
- CSS自動換行、強制不換行、強制斷行、超出顯示省略號
- 如何在Html中引入外部頁面
- reactJS入門
- React組件生命周期
- 使用React實現類似快遞單號查詢效果
- ReactJS組件生命周期詳述
- React 屬性和狀態詳解