一直以來,我都以為我已經懂了JavaScript中 閉包 的概念,直到有一次小伙伴突然問我這個概念的時候,我才發現我根本不知道該怎來麽跟他來講述這個概念。 那時候我就知道我是自我欺騙,打腫臉充胖子了。 所以,花了點時間去專門瞭解了一下,今天專門記錄一下自己所理解的閉包。 一. 概念 閉包,簡單來講, ...
一直以來,我都以為我已經懂了JavaScript中閉包的概念,直到有一次小伙伴突然問我這個概念的時候,我才發現我根本不知道該怎來麽跟他來講述這個概念。
那時候我就知道我是自我欺騙,打腫臉充胖子了。
所以,花了點時間去專門瞭解了一下,今天專門記錄一下自己所理解的閉包。
一. 概念
閉包,簡單來講,就是定義在函數內部的函數,使用閉包,可以讓你有權訪問另一個函數作用域內的變數。
所以,想要瞭解閉包的前提是,你首先要知道在JS中變數作用域的問題。
創建閉包的常見方式就是在函數內部去創建另一個函數:
function fun() {
var variable = 'Hello World';
function inner() {
console.log(variable);
}
return inner;
}
var outer = fun();
outer(); // Hello World
在這個例子中,我們想在外部用到fun()
中定義的variable
的值,但是因為變數作用域的問題,我們不可能直接取到。
所以我們採取了變通的方法:在fun()
函數內部又創建了一個函數inner()
,這時fun()
內部的variable
對於inner()
來說是可見的,既然inner()
可以取到fun()
中的變數,那麼我們將inner()
返回,就可以用到fun()
中定義的variable
了。
閉包在此處,就是鏈接函數內部和外部的一個橋梁。
在這裡提一句:如果inner()
內部存在新設置的變數,對於fun()
函數來說是不可見的,此處涉及到JS中作用鏈的問題,理解作用鏈對於徹底理解閉包的問題很有幫助,可以參考JavaScript高級程式設計(第四章)去瞭解一下作用鏈。
其實閉包的定義也就這麼簡單,對於那些過於抽象的定義,置之不理即可,不用強迫自己去理解那些比較晦澀難懂的專業定義,記住自己最終的目的並不是為了咬文嚼字,實用才是根本。
最後借用知乎上一個回答來形象的描述一下閉包的概念:
二. 閉包的用處
我總結的閉包主要用處:
- 讓外部可以讀取函數內部的變數。
- 可以封裝對象的私有屬性和私有方法。
第一點用處就是在說閉包概念時候所舉的例子。
下麵說下第二點用處:可以封裝對象的私有屬性和私有方法。
function Worker(name) {
var _salary;
function setSalary(value) {
_salary = value;
}
function getSalary() {
return _salary;
}
return {
name: name,
setSalary; setSalary;
getSalary: getSalary;
}
}
var cxk = Worker('cXK');
cxk.setSalare(100);
cxk.getSalary(); // 100
在上面的代碼中,通過閉包,_salary
變成了cxk
的私有變數。
三. 需要註意的地方
第一點需要註意的地方是關於使用閉包時記憶體的問題,因為閉包會攜帶包含它的函數的作用域,因此會比其他的函數占用更多的記憶體,濫用閉包會造成網頁的性能問題,所以對於閉包,建議只在絕對必要時在考慮使用。
對於閉包中垃圾回收的詳細測試,參考js閉包測試/司徒正美。
第二點需要註意的就是在創建閉包時可能會常犯的錯誤:在迴圈中的閉包創建問題。
function createArray() {
var result = [];
for (var i = 0; i < 10; i++) {
result[i] = function () {
return i;
}
}
return result;
}
var arr = createArray();
arr[1](); // 10
arr[2](); // 10
可以看到,跟我們預期達到的結果不一樣,每一個位置上的函數都返回了10。
這是因為每一個result[i]
上都保存著createArray()
函數的活動對象(參考JS中的作用鏈),而給result[i]
進行賦值時,'function(){return i}'
沒有執行,所以最後在arr[1]
運行時,返回的i
其實都是同一個值,即最後生成的i
,值為10。
可以做出如下修改。
修改一:在閉包里再添加一個閉包函數,同時立即執行。
function createArray() {
var result = [];
for (var i = 0; i < 10; i++) {
result[i] = function (num) {
return function () {
return num;
}
}(i)
}
return result;
}
var arr = createArray();
arr[1]();
修改二:修改var
為let
。
function createArray() {
var result = [];
for (let i = 0; i < 10; i++) {
result[i] = function () {
return i;
}
}
return result;
}
var arr = createArray();
arr[1]();
以上就是我對閉包的比較淺顯的認知,如果有不對的地方,希望能夠指正,以免我誤人子弟,謝謝。