【JS】閉包

One Day One Tip 之 閉包

時間:2016-08-30 13:39:33
作者:zhongxia

總結(jié):

概念:
閉包:能夠讀取其他函數(shù)內(nèi)部變量的函數(shù),在JavaScript中,一個函數(shù)return它內(nèi)部的一個函數(shù)。

原理:通過引用變量從而阻止該變量被垃圾回收的機(jī)制

優(yōu):

  1. 封裝私有屬性和私有方法,加強(qiáng)封裝性,可以達(dá)到對變量的保護(hù)作用。
  2. 更好的組織代碼,比如模塊化

缺:

  1. 增加了內(nèi)存的消耗,并且在某些瀏覽器下,由于垃圾回收機(jī)制不同,有可能導(dǎo)致內(nèi)存溢出
  2. 增加復(fù)雜度
  3. 由于閉包內(nèi)部變量優(yōu)先級高于外部變量,所以多查找作用域鏈中的一個層次,就會在一定程度上影響查找速度。

零、所需知識

要理解閉包,首先必須理解 JavaScript 特殊的變量作用域。
變量的作用域無非就是兩種: 全局變量局部變量

JavaScript語言的特殊之處,就在于 函數(shù)內(nèi)部可以直接讀取全局變量。
不使用 var 聲明的變量,則為全局變量。 b = 100;

function fn(){
  var a = b = 1;  
  // ==> var a = window.b = 1;  // a 是局部變量   b 是全局變量
}
fn();
console.log("b = ",b); // 1
console.log("a = ",a); //VM783:1 Uncaught ReferenceError: a is not defined

函數(shù)外部無法訪問局部變量。 因此在外部,訪問 a 變量 報錯。 而 b 變量是 全局變量,因此可以訪問到。

一、閉包是什么

閉包是概念?
閉包是指某種程序語言中的代碼塊允許一級函數(shù)存在并且在一級函數(shù)中所定義的自由變量能不被釋放,直到一級函數(shù)被釋放前,一級函數(shù)外也能應(yīng)用這些未釋放的自由變量。

我的理解就是:閉包就是能夠讀取其他函數(shù)內(nèi)部變量的函數(shù)
由于在JavaScript語言中,只有函數(shù)內(nèi)部的子函數(shù)才能讀取局部變量,因此可以把閉包簡單的理解成 『定義在一個函數(shù)內(nèi)部的函數(shù)
本質(zhì)上: 閉包就是將函數(shù)內(nèi)部和外部鏈接起來的一座橋梁

//eg: example
function a(x){
  var tmp = 10;
  return function(y){
     return (x+y)+(++tmp);
  }
}
var b = a(10);
b(5);  //26, 每執(zhí)行一次 tmp 加 1

因為 a() 執(zhí)行后,返回 的 方法 b, 內(nèi)部引用了 tmp 變量, 導(dǎo)致 tmp 變量的標(biāo)記+1, 垃圾回收機(jī)制就不會清除 tmp這個變量。
然后外部就可以繼續(xù)訪問到 tmp 變量。

二、閉包的作用

  1. 讀取函數(shù)內(nèi)部的變量
  2. 讓內(nèi)部的變量始終保持在內(nèi)存中
  3. 設(shè)計私有方法和變量【封裝框架的時候更明顯,典型的如Jquery】
var jQuery = (function(){  
  
    var jQuery = function(){  
    //TODO  
    }  
    return (window.$ = window.jQuery = jQuery);       
});  

三、閉包的優(yōu)缺點

優(yōu)點

  1. 延長作用域鏈。
  2. 更好的組織代碼,比如模塊化,異步代碼轉(zhuǎn)同步等。
  3. 加強(qiáng)封裝性,可以打到對變量的保護(hù)(第二點的加強(qiáng))
  4. 處理異步造成的變量不能即時傳遞的問題

缺點

  1. 由于閉包會使得函數(shù)中的變量都被保存在內(nèi)存中,內(nèi)存消耗很大,所以不能濫用閉包,否則會造成網(wǎng)頁的性能問題,在IE中可能導(dǎo)致內(nèi)存泄露。解決方法是,在退出函數(shù)之前,將不使用的局部變量全部刪除
  2. 增加內(nèi)存的消耗
  3. IE瀏覽器上 因為回收機(jī)制,有內(nèi)存溢出的風(fēng)險
  4. 增加了代碼復(fù)雜度

四、閉包用法實戰(zhàn)

1. 對fun的計算方式進(jìn)行定制

實際使用的時候,閉包可以創(chuàng)建出非常優(yōu)雅的設(shè)計,允許對funarg上定義的多種計算方式進(jìn)行定制。如下就是數(shù)組排序的例子,它接受一個排序條件函數(shù)作為參數(shù):

[1, 2, 3].sort(function (a, b) {
  ... // 排序條件
});

同樣的例子還有,數(shù)組的map方法是根據(jù)函數(shù)中定義的條件將原數(shù)組映射到一個新的數(shù)組中:

[1, 2, 3].map(function (element) {
  return element * 2;
}); // [2, 4, 6]

2. 函數(shù)式參數(shù)

使用函數(shù)式參數(shù),可以很方便的實現(xiàn)一個搜索方法,并且可以支持無限制的搜索條件:

someCollection.find(function (element) {
  return element.someProperty == 'searchCondition';
});

還有應(yīng)用函數(shù),比如常見的forEach方法,將函數(shù)應(yīng)用到每個數(shù)組元素:

[1, 2, 3].forEach(function (element) {
  if (element % 2 != 0) {
    alert(element);
  }
}); // 1, 3

順便提下,函數(shù)對象的 apply 和 call方法,在函數(shù)式編程中也可以用作應(yīng)用函數(shù)。 apply和call已經(jīng)在討論“this”的時候介紹過了;這里,我們將它們看作是應(yīng)用函數(shù) —— 應(yīng)用到參數(shù)中的函數(shù)(在apply中是參數(shù)列表,在call中是獨立的參數(shù)):

(function () {
  alert([].join.call(arguments, ';')); // 1;2;3
}).apply(this, [1, 2, 3]);

3. 延遲調(diào)用

閉包還有另外一個非常重要的應(yīng)用 —— 延遲調(diào)用:

var a = 10;
setTimeout(function () {
  alert(a); // 10, after one second
}, 1000);

4. 回調(diào)函數(shù)

//...
var x = 10;
// only for example
xmlHttpRequestObject.onreadystatechange = function () {
  // 當(dāng)數(shù)據(jù)就緒的時候,才會調(diào)用;
  // 這里,不論是在哪個上下文中創(chuàng)建
  // 此時變量“x”的值已經(jīng)存在了
  alert(x); // 10
};
//...

5. 創(chuàng)建封裝的作用域來隱藏輔助對象:

var foo = {};

// 初始化
(function (object) {

  var x = 10;

  object.getX = function _getX() {
    return x;
  };

})(foo);

alert(foo.getX()); // 獲得閉包 "x" – 10

參考文章

  1. 阮一峰-學(xué)習(xí)Javascript閉包(Closure)
  2. 淺析jQuery核心架構(gòu)中應(yīng)用Closure(閉包)的設(shè)計模式
  3. 用一道面試題考察對閉包的理解
  4. 深入理解JavaScript系列(16):閉包(Closures)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容