1. 程式人生 > 其它 >談談JS中的閉包

談談JS中的閉包

技術標籤:JavaScript函式閉包javascript

淺談閉包

如何產生閉包?

為了在函式外部訪問函式內區域性變數,就在父函式中定義一個子函式並讓它訪問了父函式的區域性變數,再通過父函式返回該子函式就實現了呼叫區域性變數的效果。

簡單舉例

function fun1() {
  let num = 1;
  return function fun2() {
    console.log(num++);
  }
}
const add = fun1();
add();

形成的條件

  • 函式巢狀(函式作引數,函式作返回值)
  • 內部函式訪問外部函式的區域性變數

可以把閉包理解成一個容器儲存了這些值(除錯時候看到的closure,裡面有key: value儲存閉包的值)

在案例中fun2這個函式訪問了父函式的num,函式銷燬前,num一直存在,我們每次呼叫fun2(),num值+1

閉包是什麼?

這是我從網上找到的一些的解釋。

閉包,不同於一般的函式,它允許一個函式在立即詞法作用域外呼叫時,仍可訪問非本地變數。 –維基百科

閉包就是能夠讀取其他函式內部變數的函式,閉包就是將函式內部和函式外部連線起來的一座橋樑。 –阮一峰

閉包就是對函式寫作時的環境引用。
閉包是通過JS回收機制保留某段作用域的一種手段。

兩點知識:

  • Javascript函式內部可以直接讀取全域性變數
  • 在JS中,變數查詢遵循就近原則,如果同級沒有該變數,則就一層一層向父級層查詢。—“鏈式作用域”

閉包與GC垃圾回收

JavaScript的垃圾回收機制,簡單來說就是:固定時間間隔,週期性的釋放不在使用的變數所佔記憶體。全域性變數的生命週期直至瀏覽器解除安裝頁面才會結束,區域性變數只在函式的執行過程中存在。

垃圾回收有兩個辦法:標記清除和引用計數(具體不懂)。

簡單點來說就是有沒有被引用,沒有被引用的就會被回收。

那麼在閉包中,函式呼叫了父函式作用域的值,然後被return出去了,這個函式中使用的外部值也就沒有被回收,直至函式銷燬。

function fun1() {
  let num = 1;
  return function fun2() {
    console.log(num++
); } } const add = fun1(); add();

結合上面的例子,父函式中的變數被子函式引用,子函式又被外部變數引用,可以說明閉包的產生和他們不被回收的原因。上面的列子中將全域性的add=null就可以回收fun2了。

閉包的優缺點

閉包的優點:可以在記憶體中維持一個變數,由於閉包,定義一個父函式中的變數只有通過子函式訪問到,無法通過其他途徑訪問修改,從而達到了保護變數安全的效果。

閉包的缺點:容易造成記憶體洩漏,所以不能濫用閉包。

閉包的場景

看網上還有一些場景:私有變數,安全,讓變數始終保持在記憶體中,可以避免全域性使用的變數都在window上等等,實際中的應用還不太清楚,網上資料也不多,還需磨練自己在實踐中體會。

在之前寫過的防抖和節流中,共同維護了一個定時器物件timer,其中產生了閉包,使得我們每次頻繁操作的時候,先判斷定時器狀態,再執行業務邏輯,達到防抖和節流的效果。

題目

第一道

下面兩段程式碼的執行結果是什麼?------來自阮一峰的網路日誌

var name = 'The Window';
var object = {
  name: 'My Object',

  getNameFunc: function () {
    return function () {
      console.log(this.name);
    };
  }
};
object.getNameFunc()(); // The window
var name = 'The Window';
var object = {
  name: 'My Object',

  getNameFunc: function () {
    var that = this;
    return function () {
      console.log(that.name);
    };
  }
};

object.getNameFunc()(); // My Object
// 使用了父級函式的that變數,形成閉包

第二道

以下程式碼的輸出結果是什麼?

function fun(n, o) {
  console.log(o);
  return {
    fun: function (m) {
      return fun(m, n);
    }
  };
}

console.log('-----a-----');
var a = fun(0);
a.fun(1);
a.fun(2);
a.fun(3);

console.log('-----b-----');
var b = fun(0).fun(1).fun(2).fun(3);

console.log('-----c-----');
var c = fun(0).fun(1);
c.fun(2);
c.fun(3);

執行結果
在這裡插入圖片描述