閉包是Javascript語言特有的"鏈式做用域"結構(chain scope)變量的做用域有三種:全局做用域和局部做用域以及塊做用域(ES6)。,子對象會一級一級地向上尋找全部父對象的變量。因此,父對象的全部變量,對子對象都是可見的,反之則不成立。node
閉包:JavaScript高級程序設計裏寫閉包是有權訪問另外一個函數做用域中的變量的函數,使做用域獲得了延長。咱們有時候在函數外部須要獲得函數內的局部變量。而閉包就是將函數內部和函數外部鏈接起來的一座橋樑。瀏覽器
閉包的優勢:服務器
閉包的缺點: 因爲閉包會使得函數中的變量都被保存在內存中,因此存在內存泄漏的風險閉包
使用場景 :函數內部變量只初始化一次dom
解決方法是顯式對外暴露一個接口,專門用以清理變量:函數
/*1.清除失敗,由於每次先執行mockData()後纔會執行閉包,因此每次都會在局部做用域建立常量mem*/ function mockData() { const mem = {name:"lucas",age:22}; return { clear: () => { for(let i in mem){ delete mem[i]; } }, // 顯式暴露清理接口 get: page => { if (page in mem) { return mem[page]; } mem[page] = Math.random(); } }; } console.log(mockData().get('name')); //lucas mockData().clear(); //清理變量 console.log(mockData().get('name')); //lucas /* 輸出結果 這裏執行屢次 lucas 這裏執行屢次 這裏執行屢次 lucas */ /*2.成功清除但代碼不復用*/ const mem={name:"lucas",age:22}; //卸載外面 function mockData() { console.log("這裏執行屢次") return { clear: () => { for(let i in mem){ delete mem[i]; } }, // 顯式暴露清理接口 get: (page) => { if (page in mem) { return mem[page]; } mem[page] = "dwdwd"; } }; } console.log(mockData().get('name')); //lucas mockData().clear(); //清理變量 console.log(mockData().get('name')); //undefined /* 這裏執行屢次 lucas 這裏執行屢次 這裏執行屢次 undefined */ /*3.最好寫法*/ function mockData() { const mem={name:"lucas",age:22}; console.log("執行一次") return { clear: () => { for(let i in mem){ delete mem[i]; } }, // 顯式暴露清理接口 get: (page) => { if (page in mem) { return mem[page]; } mem[page] = "dwdwd"; } }; } var result = mockData(); console.log(result.get('name')); //lucas result.clear(); //清理常量對象 console.log(result.get('name')); //undefined /* 執行一次 lucas undefined */
例如"變量只初始化一次"這樣的需求可使用下面的例子this
銷燬閉包產生的變量,實現遞增例子1設計
//經過匿名函數能夠實現閉包,簡稱匿名閉包函數 var foo = (function(varr) { var n = varr||0; return { add: function () { return ++n; }, clearVariable: function () { n = null; } } })(20); //因爲匿名當即執行函數只會執行一次,因此這裏實參數只能傳一次(若須要傳屢次請參考例子2) foo.add() //21 foo.add() //22 foo.add() //23 foo.clearVariable() //n變量被銷燬 foo.add() //1
銷燬閉包產生的變量,實現遞增例子2code
/*寫法1*/ function create_counter(initial) { var x = initial || 0; //變量只會初始化一次 return { inc: ()=> { return x++; }, clear: ()=>{ x=null; } } } var c2 = create_counter(10); c2.inc(); // 11 c2.inc(); // 12 c2.inc(); // 13 c2.clear() //x變量被銷燬 c2.inc(); // 1 /*寫法2:這樣寫不方便銷燬變量*/ function create_counter(initial) { var x = initial || 0; //變量只會初始化一次 function add(){ return x++; } return add; } var c2 = create_counter(10); c2(); //11 c2(); //12 c2(); //13 c2() = null; //清除函數也清除了變量 c2() //報錯不存在函數 var c2 = create_counter(20); c2(); //21
銷燬閉包產生的變量,實現遞增例子3對象
function Class(){ this.n = 0; this.func = ()=>{ this.n ++; return this.n; //閉包產生的變量需手動清除 } this.clear = ()=>{ return this.n=null; //銷燬函數內部的變量,避免內存泄漏 } } var obj = new Class(); obj.func() //1 obj.func() //2 obj.func() //3 obj.clear() //n變量被銷燬 obj.func() //1
後者的可擴展性更好. 當須要實現對一個變量的不一樣操做時, 後一種能夠只須要再定義一個不一樣的函數(也就是簡單線性擴展), 而前一種(閉包)則須要徹底重寫。
若是僅僅是作一個簡單的計數器,大可不用這樣麻煩。下面這簡短的代碼就能輕鬆實現。
var a = 0; function myFunction(){ a++; document.getElementById("demo").innerHTML = a; }
匿名閉包函數
var a = 1; (function test (){ alert(a); })()
上面的function均可以稱之爲閉包(匿名閉包函數)。
閉包其實還有不少實用場景,好比,咱們想在頁面上添加一些能夠調整字號的按鈕
function makeSizer(size) { return function() { document.body.style.fontSize = size + 'px'; }; } var size12 = makeSizer(12); var size14 = makeSizer(14); var size16 = makeSizer(16);