關於閉包

什麼是閉包?

閉包:

一個函數和詞法環境的引用捆綁在一起,這樣的組合就是閉包

就是一個函數A,return其內部的函數B,被return出去的函數B能夠在外部訪問A函數內部的變數(定義在函數內部的函數)

這時候就形成了一個函數B的變數背包,A函數執行結束之後這個變數背包也不會被銷毀

並且這個變數背包在A函數外部只能通過B函數來訪問(閉包就是一座將函數內部和外部連接起來的橋樑)

原理:

作用域鏈,當前作用域鏈可以訪問上級作用域中的變數

閉包解決的問題:

能夠讓函數作用域中的變數在函數執行結束之後不被銷毀,同時也能在函數外部可以訪問函數內部的局部變數。

閉包帶來的問題:

由於垃圾回收機制不會將閉包中的變數銷毀,記憶體消耗會很大,所以不能濫用閉包,否則會造成網頁性能問題,

也可能導致記憶體泄露,記憶體泄露積累多了就容易導致記憶體溢出(解決辦法是在退出函數之前,將不使用的局部變數全部刪除)

閉包的主要作用:

模仿塊級作用域:

已知數組var arr = ['a','b','c','d','e','f'],每過1秒列印一下數組中的值:

未有塊級作用域時:setTimeout屬於非同步任務,for循環結束後,每秒輸出的都為 'e'

 

var arr = ["a", "b", "c", "d", "e"];
for (var i = 0; i < arr.length; i++) {
    var item = arr[i];
    setTimeout(function() {
      console.log(item);
    }, 1000 * (i + 1));
}

 

ES5:藉助IIFE(彌補JS在scope方面的缺陷)

 

var arr = ["a", "b", "c", "d", "e"];
for (var i = 0; i < arr.length; i++) {
    (function(j){
        var item = arr[j];
        setTimeout(function() {
            console.log(item);
        }, 1000 * (j + 1));
    })(i);
}    

 

ES6:let、const塊級作用域

 

var arr = ["a", "b", "c", "d", "e"];
for (let i = 0; i < arr.length; i++) {
    const item = arr[i];
    setTimeout(function() {
        console.log(item);
    }, 1000 * (i + 1));
}        

 

實現柯里化:

柯里化是一種轉換,將 f(a,b,c) 轉換為可以被以 f(a)(b)(c) 的形式進行調用。

在構造函數中定義特權方法:

有權訪問私有變數、私有函數的公有方法就是特權方法

Vue中數據響應式Observe中使用閉包等:

function defineRealive(target, key, value){
    return Object.defineProperty(target, key, {
        get(){
            console.log(`通過getter獲取數據:${value}`);
            return value;
        },
        set(val){
            console.log(`通過setter設置數據:新值-${val};舊值-${value}`);
            // 形參也是一個普通的局部變數,只是可能我們平時使用的時候,一般不會對形參進行賦值操作,因為大部分情況,形參都是外部傳入的數據,我們無需修改。
            // 這裡就用到了閉包的原理,value是外層函數defineRealive的參數,而我們實際上使用value確是在內層的get或set方法裡面
            // 這樣就形成了一個閉包的結構了。根據閉包的特性,內層函數可以引用外層函數的變數,並且當內層保持引用關係時外層函數的這個變數
            // 不會被垃圾回收機制回收。那麼,我們在設置值的時候,把val保存在value變數當中,然後get的時候再通過value去獲取,這樣,我們再訪問
            // obj.name,無論是設置值還是獲取值,實際上都是對value這個形參進行操作的。
            value = val;
        }
    });
}
 
let obj = {
    name: 'kiner',
    age: 20
};
 
Object.keys(obj).forEach(key=>defineRealive(obj, key, obj[key]));

obj.name = 'kanger';
obj.age = 18;
// 控制台輸出:kanger 18
console.log(obj.name,obj.age);

 

記憶體泄露與記憶體溢出:

記憶體泄露是指程式中已動態分配的堆記憶體由於某種原因,程式未釋放或無法釋放,造成的記憶體浪費

導致程式運行速度減慢甚至系統崩潰等嚴重後果(即不再會被使用的對象的記憶體不能被回收)

記憶體泄後堆積的結果就是記憶體溢出

 

參考:

面試閉包題

Javascript柯里化

特權方法

Vue對於閉包的使用

Tags: