整理一下對閉包的理解
閉包foo
var fn = null; //定義全局變量 fn
function foo() {
var a = 2;
function innerFoo() { //定義內部函數 innerFoo
console.log(a);
}
fn = innerFoo; //將 innerFoo 賦值給全局變量 fn
}
foo(); // undefined
fn(); // 2
為什么要用閉包呢?
- 我們想要讀取其他函數內部(當前作用域之外)變量,如訪問函數 foo 內部的變量 a
- 當函數 foo 執行完畢后,生命周期結束,其函數的上下文失去引用,占用的內存空間很快就會被垃圾回收期釋放,閉包會阻止這一過程
- 盡可能少的全局變量。
console
什么是閉包呢?
當函數(內部函數)可以記住并訪問所在的作用域(全局作用域除外)時,就產生了閉包,使函數在當前作用域外執行。
在 JavaScript 中內部函數可以訪問外部函數的上下文。
在例文代碼中閉包是什么呢?
foo 是閉包(Chrome 瀏覽器認為),而不是之前一直認為的 innerFoo 。
閉包的應用
延遲函數
、柯里化
、模塊
以延遲函數為例:
function fn() {
console.log("hello world");
}
var timer = setTimeout(fn,1000);
timer;
console_2
執行 timer 時,返回這個 setTimeout 的 ID,表明 setTimeout 函數本身已經執行完畢。一秒鐘之后,fn (保存在 setTimeout 的變量對象中)才會執行。
按理來說一秒后 setTimeout 執行結束后,fn 也就不存在了,但是事實上依舊輸出了 hello world。
這正是由于閉包。setTimeout 保留了 fn 的引用,使 fn 沒有在其執行完畢后被垃圾收集器回收。由此 setTimeout 執行結束后一秒,我們仍能使用 fn 。
帶來的問題-內存泄露
閉包會使得函數中的變量都被保存在內存中,內存消耗很大,所以不能濫用閉包,否則會造成網頁的性能問題。
由于
IE
的js
對象和DOM
對象使用不同的垃圾收集方法,因此閉包在IE中會導致內存泄露問題,也就是無法銷毀駐留在內存中的元素。解決思路:在函數之前,將不使用的局部變量全部刪除。
賦值 null,解除引用
function closure(){
var element = document.getElementById('someElement');//element用完之后一直駐留在內存中
element.onclick = function () {
alert('element.id');//這里用element導致內存泄露
};
}
closure();
//解決
function closure(){
var element = document.getElementById('someElement');
var id = element.id; //將element.id保存在一個變量中,然后在內部函數中引用,消除對該變量的循環引用。
element.onclick = function () {
alert('id');
};
element = null; //將element解除引用來避免內存泄露
}
- 在外面包一層匿名自執行函數
(function () {
...//閉包
})();
//好處:不會污染全局變量,在匿名函數執行完了里面的的變量就釋放掉了
相關輸出題目
for (var i=0;i<5;i++){
setTimeout(function(){
console.log(i++)
},1000);
}
// 5,6,7,8,9
for (var i=0;i<5;i++){
setTimeout(function(){
console.log(++i)
},1000);
}
// 6,7,8,9,10
for (let i=0;i<5;i++){
setTimeout(function(){
console.log(i++)
},1000);
}
// 0,1,2,3,4
for (var i=0;i<5;i++){
(function(x){
setTimeout(function(){
console.log(x++)
},1000);
})(i)
}
// 0,1,2,3,4