閉包

閉包,獲取一個局部做用域裏變量的訪問權限,涉及到做用域棧、執行上下文、垃圾回收機制、內存駐留以及性能問題,閉包切斷做用域棧產生的垃圾回收事件,實現變量的內存駐留。主要應用場景在須要累積效應、重複循環事件、先後事件相關等。於是,爲避免產生嚴重的性能問題,在完成事件任務後要把閉包置爲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);
  1. 示例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();
  1. 示例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
相關文章
相關標籤/搜索