闭包,获取一个局部作用域里变量的访问权限,涉及到作用域栈、执行上下文、垃圾回收机制、内存驻留以及性能问题,闭包切断作用域栈产生的垃圾回收事件,实现变量的内存驻留。主要应用场景在需要累积效应、重复循环事件、前后事件相关等。因而,为避免产生严重的性能问题,在完成事件任务后要把闭包置为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);
  • 示例2

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. 示例4

 function badClosureExample() {
    var as = document.querySelectorAll('a');
    for (var i = 0; i < 4; i++) {
        (function (i) {
            as[i].onclick = function () {
                alert('单击第' + i + '个');
            }
        })(i);
    }
}
badClosureExample();

1、将变量 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();

2、将变量 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();

3、加一层闭包,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();

4、加一层闭包,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();

5、加一层闭包,返回一个函数作为响应事件(注意与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();

6、用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();

7、用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

寒青
10.4k 声望3.8k 粉丝