閉包的特性javascript
1.函數嵌套函數 2.函數內部能夠引用外部的參數和變量 3.參數和變量不會被垃圾回收機制回收
閉包的缺點就是常駐內存,會增大內存使用量,使用不當很容易形成內存泄露,主要用於私有的方法和變量php
Javascript
的垃圾回收原理基本上全部語言自動內存管理,用的都是引用計數了java
一、在javascript中,若是一個對象再也不被引用,那麼這個對象就會被GC回收; 二、若是兩個對象互相引用,而再也不被第3者所引用,那麼這兩個互相引用的對象也會被回收。
閉包的好處json
1.但願一個變量長期駐紮在內存中 2.避免全局變量的污染 3.私有成員的存在
一、變量的累加,常駐內存segmentfault
示例一閉包
var a = 1; function abc(){ a++; alert(a); } abc(); //2 abc(); //3
示例二模塊化
function abc(){ var a = 1; a++; alert(a); } abc(); //2 abc(); //2
那麼怎麼才能作到變量a既是局部變量又能夠累加呢?函數
function outer(){ var x=10; return function(){ //函數嵌套函數 x++; alert(x); } } var y = outer(); //外部函數賦給變量y; y(); //y函數調用一次,結果爲11,至關於outer()(); y(); //y函數調用第二次,結果爲12,實現了累加
二、模塊化代碼,減小全局變量的污染this
var abc = (function(){ //abc爲外部匿名函數的返回值 var a = 1; return function(){ a++; alert(a); } })(); abc(); //2 ;調用一次abc函數,實際上是調用裏面內部函數的返回值 abc(); //3
三、私有成員的存在spa
var aaa = (function(){ var a = 1; function bbb(){ a++; alert(a); } function ccc(){ a++; alert(a); } return { b:bbb, //json結構 c:ccc } })(); aaa.b(); //2 aaa.c() //3
四、內存泄露問題
因爲IE
的js
對象和DOM
對象使用不一樣的垃圾收集方法,所以閉包在IE
中會致使內存泄露問題,也就是沒法銷燬駐留在內存中的元素
function closure(){ var oDiv = document.getElementById('oDiv');//oDiv用完以後一直駐留在內存中 oDiv.onclick = function () { alert('oDiv.innerHTML');//這裏用oDiv致使內存泄露 }; } closure(); //最後應將oDiv解除引用來避免內存泄露 function closure(){ var oDiv = document.getElementById('oDiv'); var test = oDiv.innerHTML; oDiv.onclick = function () { alert(test); }; oDiv = null; }
五、閉包引發的誤解 http://segmentfault.com/q/1010000003490094 原文地址
錯誤的寫法
for(var i = 0; i < 10; i++) { setTimeout(function() { console.log(i); }, 1000); } // 9 10次
正確的寫法1
for(var i = 0; i < 10; i++) { (function(e) { setTimeout(function() { console.log(e); }, 1000); })(i); }
正確的寫法2
for(var i = 0; i < 10; i++) { setTimeout((function(e) { return function() { console.log(e); } })(i), 1000) }
正確的寫法3
for(var i = 0; i < 10; i++) { setTimeout(function(e) { console.log(e); }, 1000, i); }
正確的寫法4 Some legacy JS environments (Internet Explorer 9 & below) do not support this.
for(var i = 0; i < 10; i++) { setTimeout(console.log.bind(console, i), 1000); }
正確的寫法5 固然在ES3/5下除了經過IIFE構造做用域外,還能夠經過with來構造
for(var i = 0; i < 10; ++i) with({i:i}){ setTimeout(function(){console.log(i)}, 1000) }
正確的寫法6 使用let表示變量做用域
for(let i = 0; i < 10; ++i){ setTimeout(function(){console.log(i)}, 1000) }
解決這個問題的關鍵:弄清楚每種寫法的做用域鏈
最開始的寫法 for =》做用域A,setTimeout=》做用域B,閉包的存在是得外部代碼執行完畢,其任然駐留在內存中,代碼塊B在執行時,找不到變量i,因而沿着做用域鏈向上找,取到A做用域中i的值,此時內存中i值爲10
for(var i = 0; i < 10; i++) { // 做用域A,存儲i的值 setTimeout(function() {//做用域B console.log(i); }, 1000); }
而正確的寫法
for(var i = 0; i < 10; i++) { //做用域A,存儲i (function(e) { //做用域B0,存儲e0 做用域B1,存儲e1,每循環一次,都有一個單獨的做用域 setTimeout(function() {//做用域C0,C1,C2,... 對應外部做用域B0,B1... console.log(e); }, 1000); })(i); }
參考地址
http://segmentfault.com/q/1010000003490094