淺談閉包、閉包的用途以及如何防止閉包產生內存洩漏

什麼是閉包

淺談閉包、閉包的用途以及如何防止閉包產生內存洩漏

MDN的解釋:閉包是函數和聲明該函數的詞法環境的組合。

簡單講,閉包就是指有權訪問另一個函數作用域中的變量的函數。

它由兩部分構成:函數,以及創建該函數的環境。環境由閉包創建時在作用域中的所有局部變量組成。

理解閉包的關鍵在於:外部函數調用之後其變量對象本應該被銷燬,但閉包的存在使我們仍然可以訪問外部函數的變量對象,這就是閉包的重要概念。

如何產生一個閉包函數

創建閉包最常見方式,就是在一個函數內部創建另一個函數。

function outer() {

let name = "hello"; // 閉包創建時所能訪問的局部變量

function sayHello() { // 閉包函數

alert(name);

}

return sayHello; // 返回閉包函數

}

let myFunc = outer();

myFunc();

閉包的作用域鏈包含著它自己的作用域,以及包含它的函數的作用域和全局作用域。

outer有了myFunc的引用,內存一直得不到釋放,咋辦呢?這樣的函數多了是不是會造成內存溢出?

手動釋放一下:

myFunc = null;

閉包的注意事項(如何防止內存洩漏)

通常,函數的作用域及其所有變量都會在函數執行結束後被銷燬。但是,在創建了一個閉包以後,這個函數的作用域就會一直保存到閉包不存在為止。

function makeAdder(x) {

return function(y) {

return x + y;

};

}

let add5 = makeAdder(5);

let add10 = makeAdder(10);

console.log(add5(2)); // 7

console.log(add10(2)); // 12

add5 = null;

add10 = null;

add5 和 add10 都是閉包。它們共享相同的函數定義,但是保存了不同的詞法環境。在 add5 的環境中,x 為 5。而在 add10 中,x 則為 10。

最後通過 null 釋放了 add5 和 add10 對閉包的引用。

在javascript中,如果一個對象不再被引用,那麼這個對象就會被垃圾回收機制回收;

如果兩個對象互相引用,而不再被第3者所引用,那麼這兩個互相引用的對象也會被回收。

閉包中的this對象

let name = "window";

let obj = {

name: 'object',

getName: function() {

return function() {

return this.name;

}

}

}

obj.getName()(); // window

在上面這段代碼中,obj.getName()()實際上是在全局作用域中調用了匿名函數,this指向了window。

window才是匿名函數功能執行的環境。

如果想使this指向外部函數的執行環境,可以這樣改寫:

let name = "window";

let obj = {

name: 'object',

getName: function() {

var that = this;

return function() {

return that.name;

}

}

}

obj.getName()();

函數內部的定時器

當函數內部的定時器引用了外部函數的變量對象時,該變量對象不會被銷燬。

(function() {

let a = 0;

setInterval(function(){

console.log(a++);

}, 1000)

})()

閉包的用途

  • 模擬塊級作用域

var isShow = true;

if(isShow){

var a=1000;

console.log(a);

}

console.log(a); // 在if定義的變量在外部可以訪問

(function(){ // a在外部就不認識啦

var isShow = true;

if(isShow){

var a=10000;

console.log(a);

}

})();

console.log(a); // 報錯,無法訪問

  • 讓變量的值始終保持在內存中,對結果進行緩存

function fn(){

let count = 0;

return function(){

count++;

return count;

}

}

let add=fn();

add(); // 1

add(); // 2

add(); // 3

  • 封裝工具函數

let counter = (function(){

let privateCounter = 0; // 私有變量

function change(val){

privateCounter += val;

}

return {

increment:function(){ // 三個閉包共享一個詞法環境

change(1);

},

decrement:function(){

change(-1);

},

value:function(){

return privateCounter;

}

};

})();

counter.value(); // 0

counter.increment();

counter.value(); // 1


經典前端面試題每日更新,歡迎參與討論,地址:https://github.com/daily-interview/fe-interview。


更多angular1/2/4/5、ionic1/2/3、react、vue、微信小程序、nodejs等技術文章、視頻教程和開源項目,請關注微信公眾號——全棧弄潮兒。

淺談閉包、閉包的用途以及如何防止閉包產生內存洩漏


分享到:


相關文章: