> 閉包(closure)是javascript的一大難點,也是它的特色。很多高級應用都要依靠閉包來實現。
要理解閉包,首先要理解javascript的全局變量和局部變量。
> javascript語言的特別之處就在于:函數內部可以直接讀取全局變量,但是在函數外部無法讀取函數內部的局部變量。
```
function f1(){
var a=10;
function f2(){
alert(a); // 10
}
}
```
## 如何從外部讀取函數內部的局部變量?
我們有時候需要獲取到函數內部的局部變量,正常情況下,這是辦不到的!只有通過變通的方法才能實現。那就是在函數內部,再定義一個函數。
## 閉包的概念
上面代碼中的f2函數,就是閉包。
各種專業文獻的閉包定義都非常抽象,我的理解是: 閉包就是能夠讀取其他函數內部變量的函數。
由于在javascript中,只有函數內部的子函數才能讀取局部變量,所以說,閉包可以簡單理解成“定義在一個函數內部的函數“。
所以,在本質上,閉包是將函數內部和函數外部連接起來的橋梁。
## 閉包的用途
閉包可以用在許多地方。它的最大用處有兩個,一個是前面提到的可以讀取函數內部的變量,另一個就是讓這些變量的值始終保持在內存中,不會在f1調用后被自動清除。
為什么會這樣呢?原因就在于f1是f2的父函數,而f2被賦給了一個全局變量,這導致f2始終在內存中,而f2的存在依賴于f1,因此f1也始終在內存中,不會在調用結束后,被垃圾回收機制(garbage collection)回收。
> 在我們平時的代碼中經常會用到閉包,比如在構造函數中
```
function a(){
var n = 0;
this.add = function () {
n++;
console.log(n);
};
}
var c = new a();
c.add(); //控制臺輸出1
c.add(); //控制臺輸出2
//另一種寫法
function a(){
this.n = 0,
this.add = function () {
this.n++;
console.log(this.n);
};
}
var c = new a();
c.add(); //控制臺輸出1
c.add(); //控制臺輸出2
```
```
//常見閉包的寫法
function a(){
var n = 0;
function add(){
n++;
console.log(n);
}
return add;
}
var a1 = a(); //注意,函數名只是一個標識(指向函數的指針),而()才是執行函數;
a1(); //控制臺輸出1
a1(); //控制臺輸出2
```
```
//另一種調用方法
function a(){
var n = 0;
function add(){
n++;
console.log(n);
}
return add;
}
a()();
//定義函數并立即調用
var a = (function() {
var n = 0;
return function() {
n++;
console.log(n);
};
}());
a()
```
## 閉包的實際應用
> 使用閉包,我們可以做很多事情。比如模擬面向對象的代碼風格;更優雅,更簡潔的表達出代碼;在某些方面提升代碼的執行效率。
```
//封裝
var person = function(){
//變量作用域為函數內部,外部無法訪問
var name = "張三";
return {
getName : function(){
return name;
},
setName : function(newName){
name = newName;
}
}
}();
Console.log(person.name);
通過person.name是無法獲取到name的值,如果要獲取到name的值可以通過
Console.log(person.getName()); //直接獲取到 張三
person.setName("李四"); //重新設置新的名字
print(person.getName()); //獲取 李四
```
```
//繼承
function Person(){
var name = "張三";
return {
getName : function(){
return name;
},
setName : function(newName){
name = newName;
}
}
};
var BlackPeople = function(){};
//黑人繼承自Person
BlackPeople.prototype = new Person();
var p = new BlackPeople();
p.setName("Tom");
console.log(p.getName()); // Tom
```
## 總結
> 閉包就是一個函數引用另外一個函數的變量,因為變量被引用著所以不會被回收,因此可以用來封裝一個私有變量。這是優點也是缺點,不必要的閉包只會徒增內存消耗!
- 1.JS的基礎知識
- (1)調試
- (2)變量
- (3)數據類型
- 數據類型之間的轉換
- (4)全局變量和局部變量
- (5)運算符和表達式
- (6)數組
- 2.控制語句DOM,BOM,事件
- (1)控制語句
- (2)DOM的基礎
- 節點
- 改變樣式
- DOM事件
- 3.函數
- (1)聲明函數
- (2)構造函數
- (3)函數的參數
- (4)函數的傳參
- (5)改變this
- (6)重載
- (7)回調函數
- 4.數組
- (1)創建數組
- (2)增刪改查
- (3)字符串與數組的轉換
- 5.正則
- (1)創建正則
- (2)字符串中支持正則
- (3)語法
- 最核心的元字符
- 6.ajax
- (1)原生ajax
- (2)http,get,post
- (3)跨域
- (4)jQuery-ajax
- (5)axios
- 7.面向對象
- (1)原型
- (2)原型鏈,繼承
- (3)多態
- 8.es6小結
- 9.js+canvas實現驗證碼
- 10.js的作用域
- 11.閉包
- 實例
- toggle
- 圖片切換
- swiper
- 遮罩顏色漸變
- 表格添加
- 瀑布流
- ajax數據請求渲染
- 百度地圖