閉包,獲取一個局部做用域裏變量的訪問權限,涉及到做用域棧、執行上下文、垃圾回收機制、內存駐留以及性能問題,閉包切斷做用域棧產生的垃圾回收事件,實現變量的內存駐留。主要應用場景在須要累積效應、重複循環事件、先後事件相關等。於是,爲避免產生嚴重的性能問題,在完成事件任務後要把閉包置爲null,釋放內存。閉包
這裏介紹JS中做用域棧的特性,即先進後出,全局做用域位於棧底,局部做用域按照編譯、執行順序依次入棧,執行完畢依次出棧,對變量進行垃圾回收,釋放內存。瞭解此特性,利用全局做用域始終位於棧底,而且老是最後完成垃圾回收,只要在局部做用域中裝載具備全局效應的做用域,阻斷垃圾回收,就完成了閉包的設計。函數
示例1性能
function foo(x) { var tmp = 3; return function (y) { console.log(x + y + tmp); x.memb = x.memb ? x.memb + 1 : 1; console.log(x.memb); } } var age = new Number(2); var bar = foo(age); // bar 如今是一個引用了age的閉包 bar(10);
示例2this
function foo(x) { var temp = 3; return function (y) { console.log(x + y + (++temp)); } } var bar = foo(2); bar(10);
示例3設計
function badClosureExample() { var as = document.querySelectorAll('a'); for (var i = 0; i < 4; i++) { as[i].onclick = new popNum(i); function popNum(oNum) { return function () { alert('單擊第' + oNum + '個'); } } } } badClosureExample();
示例4code
function badClosureExample() { var as = document.querySelectorAll('a'); for (var i = 0; i < 4; i++) { (function (i) { as[i].onclick = function () { alert('單擊第' + i + '個'); } })(i); } } badClosureExample();
一、將變量 i
保存給在每一個段落對象(p
)上對象
function init() { var pAry = document.getElementsByTagName("p"); for (var i = 0; i < pAry.length; i++) { pAry[i].i = i; pAry[i].onclick = function () { alert(this.i); } } } init();
二、將變量 i
保存在匿名函數自身事件
function init() { var pAry = document.getElementsByTagName("p"); for (var i = 0; i < pAry.length; i++) { (pAry[i].onclick = function () { alert(arguments.callee.i); }).i = i; } } init();
三、加一層閉包,i
以函數參數形式傳遞給內層函數內存
function init() { var pAry = document.getElementsByTagName("p"); for (var i = 0; i < pAry.length; i++) { (function (i) { pAry[i].onclick = function () { alert(i); } })(i);//調用時參數 } } init();
四、加一層閉包,i
以局部變量形式傳遞給內層函數作用域
function init() { var pAry = document.getElementsByTagName("p"); for (var i = 0; i < pAry.length; i++) { (function () { var index = i;//調用時局部變量 pAry[i].onclick = function () { alert(index); } })(); } } init();
五、加一層閉包,返回一個函數做爲響應事件(注意與3的細微區別)
function init() { var pAry = document.getElementsByTagName("p"); for (var i = 0; i < pAry.length; i++) { pAry[i].onclick = function (i) { return function () { //返回一個函數 alert(i); } }(i) } } init();
六、用Function
實現,實際上每產生一個函數實例就會產生一個閉包
function init() { var pAry = document.getElementsByTagName("p"); for (var i = 0; i < pAry.length; i++) { pAry[i].onclick = new Function("alert(" + i + ")"); //new一次就產生一個函數實例 } } init();
七、用Function
實現,注意與6的區別
function init() { var pAry = document.getElementsByTagName("p"); for (var i = 0; i < pAry.length; i++) { pAry[i].onclick =Function("alert(" + i + ")"); } } init();
示例5
var name = "The Window"; var object = { name: "My Object", getNameFunc: function () { return function () { return this.name; }; } }; alert(object.getNameFunc()()); //The Window
示例6
function outerFun() { var a = 0; function innerFun() { a++; alert(a); } return innerFun; //注意這裏 } var obj = outerFun(); obj(); //結果爲1 obj(); //結果爲2