關於JS中閉包的問題
一直以來,我都以為我已經懂了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中作用鏈的問題,理解作用鏈對於徹底理解閉包的問題很有幫助,可以參考
其實閉包的定義也就這麼簡單,對於那些過於抽象的定義,置之不理即可,不用強迫自己去理解那些比較晦澀難懂的專業定義,記住自己最終的目的並不是為了咬文嚼字,實用才是根本。
最後借用知乎上一個回答來形象的描述一下閉包的概念:
二. 閉包的用處
我總結的閉包主要用處:
- 讓外部可以讀取函式內部的變數。
- 可以封裝物件的私有屬性和私有方法。
第一點用處就是再說閉包概念時候所舉的例子。
下面說下第二點用處:可以封裝物件的私有屬性和私有方法。
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]();
以上就是我對閉包的比較淺顯的認知,如果有不對的地方,希望能夠指正,以免我誤人子弟,謝謝。