# One Day One Tip 之 **閉包**
>CreateTime:2016-08-30 13:39:33
>Author:zhongxia
## 總結:
**概念:**
閉包:能夠讀取其他函數內部變量的函數,在JavaScript中,一個函數return它內部的一個函數。
原理:**通過引用變量從而阻止該變量被垃圾回收的機制**
**優:**
1. 封裝私有屬性和私有方法,加強封裝性,可以達到對變量的保護作用。
2. 更好的組織代碼,比如模塊化
**缺:**
1. 增加了內存的消耗,并且在某些瀏覽器下,由于垃圾回收機制不同,有可能導致內存溢出
2. 增加復雜度
3. 由于閉包內部變量優先級高于外部變量,所以多查找作用域鏈中的一個層次,就會在一定程度上影響查找速度。
## 零、所需知識
要理解閉包,**首先必須理解 JavaScript 特殊的變量作用域。**
變量的作用域無非就是兩種: **全局變量** 和 **局部變量**
JavaScript語言的特殊之處,就在于 函數內部可以直接讀取全局變量。
不使用 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
~~~
函數外部無法訪問局部變量。 因此在外部,訪問 a 變量 報錯。 而 b 變量是 全局變量,因此可以訪問到。
## 一、閉包是什么
>**閉包是概念?**
閉包是指某種程序語言中的代碼塊允許一級函數存在并且在一級函數中所定義的自由變量能不被釋放,直到一級函數被釋放前,一級函數外也能應用這些未釋放的自由變量。
>我的理解就是:**閉包就是能夠讀取其他函數內部變量的函數**
由于在JavaScript語言中,只有函數內部的子函數才能讀取局部變量,因此可以把閉包簡單的理解成 『**定義在一個函數內部的函數**』
本質上: **閉包就是將函數內部和外部鏈接起來的一座橋梁**。
~~~
//eg: example
function a(x){
var tmp = 10;
return function(y){
return (x+y)+(++tmp);
}
}
var b = a(10);
b(5); //26, 每執行一次 tmp 加 1
~~~
因為 a() 執行后,返回 的 方法 b, 內部引用了 tmp 變量, 導致 tmp 變量的標記+1, 垃圾回收機制就不會清除 tmp這個變量。
然后外部就可以繼續訪問到 tmp 變量。
## 二、閉包的作用
1. 讀取函數內部的變量
2. 讓內部的變量始終保持在內存中
3. 設計私有方法和變量【封裝框架的時候更明顯,典型的如Jquery】
~~~
var jQuery = (function(){
var jQuery = function(){
//TODO
}
return (window.$ = window.jQuery = jQuery);
});
~~~
## 三、閉包的優缺點
### **優點**
1. 延長作用域鏈。
2. 更好的組織代碼,比如模塊化,異步代碼轉同步等。
3. 加強封裝性,可以打到對變量的保護(第二點的加強)
4. 處理異步造成的變量不能即時傳遞的問題
### **缺點**
1. 由于閉包會使得函數中的變量都被保存在內存中,內存消耗很大,所以不能濫用閉包,否則會造成網頁的性能問題,在IE中可能導致內存泄露。解決方法是,在退出函數之前,將不使用的局部變量全部刪除
2. 增加內存的消耗
3. IE瀏覽器上 因為回收機制,有內存溢出的風險
4. 增加了代碼復雜度
## 四、閉包用法實戰
### 1. 對fun的計算方式進行定制
實際使用的時候,閉包可以創建出非常優雅的設計,允許對funarg上定義的多種計算方式進行定制。如下就是數組排序的例子,它接受一個排序條件函數作為參數:
~~~
[1, 2, 3].sort(function (a, b) {
... // 排序條件
});
~~~
同樣的例子還有,數組的map方法是根據函數中定義的條件將原數組映射到一個新的數組中:
~~~
[1, 2, 3].map(function (element) {
return element * 2;
}); // [2, 4, 6]
~~~
#### 2. 函數式參數
使用函數式參數,可以很方便的實現一個搜索方法,并且可以支持無限制的搜索條件:
~~~
someCollection.find(function (element) {
return element.someProperty == 'searchCondition';
});
~~~
還有應用函數,比如常見的forEach方法,將函數應用到每個數組元素:
~~~
[1, 2, 3].forEach(function (element) {
if (element % 2 != 0) {
alert(element);
}
}); // 1, 3
~~~
順便提下,函數對象的 apply 和 call方法,在函數式編程中也可以用作應用函數。 apply和call已經在討論“this”的時候介紹過了;這里,我們將它們看作是應用函數 —— 應用到參數中的函數(在apply中是參數列表,在call中是獨立的參數):
~~~
(function () {
alert([].join.call(arguments, ';')); // 1;2;3
}).apply(this, [1, 2, 3]);
~~~
#### 3. 延遲調用
閉包還有另外一個非常重要的應用 —— 延遲調用:
~~~
var a = 10;
setTimeout(function () {
alert(a); // 10, after one second
}, 1000);
~~~
#### 4. 回調函數
~~~
//...
var x = 10;
// only for example
xmlHttpRequestObject.onreadystatechange = function () {
// 當數據就緒的時候,才會調用;
// 這里,不論是在哪個上下文中創建
// 此時變量“x”的值已經存在了
alert(x); // 10
};
//...
~~~
####5. 創建封裝的作用域來隱藏輔助對象:
~~~
var foo = {};
// 初始化
(function (object) {
var x = 10;
object.getX = function _getX() {
return x;
};
})(foo);
alert(foo.getX()); // 獲得閉包 "x" – 10
~~~
## 參考文章
1. [阮一峰-學習Javascript閉包(Closure)](http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html)
2. [淺析jQuery核心架構中應用Closure(閉包)的設計模式](http://blog.csdn.net/liaozhongping/article/details/47058485)
3. [用一道面試題考察對閉包的理解](http://hao.jser.com/archive/5308/)
4. [深入理解JavaScript系列(16):閉包(Closures)](http://www.cnblogs.com/TomXu/archive/2012/01/31/2330252.html)
- 前言
- 【00】如何寫
- 【STAT法則寫簡歷】
- 【01】前端
- 【20160829 前端面試題】
- 【騰訊IMWeb】筆試題(沒有答案)
- 【桑世龍】前端筆試題(沒有答案)
- 【瀏覽器輸入URL后發生了什么】
- 【JS截圖并生成圖片】
- 【20160924】Sass 入門
- 【02】技巧
- 【01】GOOGLE搜索技巧
- 【02】Chrome跨域訪問線上接口
- 【One Day One Tip】
- 【20160830】~ 閉包
- 【20160831】~ 繼承的幾種實現方式
- 【20160901】~瀏覽器輸入URL到頁面展示完成,發生了什么?(一)
- 【20160902】~瀏覽器輸入URL,發生過程系列(轉載)
- 【20160903】~ video在不同平臺下的差異性
- 【20160906】~webpack之sourceMap
- 【20160909】ACE自定義代碼提示
- 【20160910】Mac Nw.js 環境安裝
- 【99】轉載筆記
- 用一道面試題考察對閉包的理解