闭包

2016-10-12 曹强 更多博文 » 博客 » GitHub »

javascript

原文链接 https://ronghuaxueleng.github.io/2016/10/12/JavaScript%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E4%B8%8E%E5%BC%80%E5%8F%91%E5%AE%9E%E8%B7%B5-2%E9%97%AD%E5%8C%85/
注:以下为加速网络访问所做的原文缓存,经过重新格式化,可能存在格式方面的问题,或偶有遗漏信息,请以原文为准。


闭包是js中一个难懂又必须征服的概念,他的形成与变量作用域以及变量的生存周期密切相关。

变量作用域和生存周期

作用域,按字面理解,就是指变量的有效范围,超出这个范围就无法访问。
在函数中,里面函数可以访问外面的变量,但是外面无法访问内部变量。举个简单例子:

var a = 1;
var fun1 = function() {
    var b = 2;
    var fun2 = function() {
        var c = 3;
        alert(b); //2
        alert(c); //3
    }
    fun2();
    alert(c); //c is not defined
};
fun1();

生存周期:全局变量的生存周期是永久的,除非我们主动销毁,但是普通对于用var声明的局部变量,当退出函数不再使用时,就会被销毁。但是当你使用到闭包时,情况就不一样了

var fun = function() {
    var a = 1;
    return function() {
        a++;
        alert(a);
    }
};
var f = fun();
f(); //2
f(); //3
f(); //4
f(); //5

解释:当执行var f = fun();时,f返回了一个匿名函数的引用,它可以访问到fun()被调用的生产环境,而局部变量a一直在这个环境中。既然局部变量能 被外界访问,那它就有了不被销毁的理由,于是局部变量生命被延续了。

再例如:

var nodes = document.getElemmentByTagName('div'); //此处他的长度为5
for (var i = 0, len = nodes.length; i < len; i++) {
    nodes[i].onclick = function() {
        alert(i);
    }
};

以上代码无论点哪个div显示的都是5(div节点的onclick是异步触发的,当事件触发时,循环已经结束,此时i的值是5)
解决方法:闭包大法好

for (var i = 0, len = nodes.length; i < len; i++) {
    (function(i) {
        nodes[i].onclick = function() {
            alert(i);
        }
    })(i)
};

通常,闭包可以有封装一些不需要暴露在全局的变量,延续局部变量寿命等等用途。

命令模式

命令模式的意图是把请求封装为对象,从而分离请求的发起者和请求的接收者(执行者)之间的耦合关系。在命令执行之前可预先往命令对象中植入命令的接收者。

var TV = {
    open: function() {
        alert('open');
    },
    close: function() {
        alert('close');
    }
};

var createCommand = function(receiver) {
    var execute= function() {
        return receiver.open(); //执行命令打开电视
    }

    var undo= function() {
        return receiver.close(); //执行命令关闭电视
    }

    return {
        execute: execute,
        undo: undo
    }
};

var setComment = function(command) {
    document.getElementById('execute').onclick = function() {
        command.execute();
    }
    document.getElementById('undo').onclick = function() {
        command.undo();
    }
};
setCommand(createCommand(tv));