前言

闭包是js特有一个概念问题,应该是js学习者都会遇到的疑问:什么是闭包? 关于闭包的回答网上也有很多前辈的总结,笔者也看的比较多,都是从不同角度去阐述闭包这个问题。 笔者最近复习闭包的概念,从js执行上下文角度去理解闭包,从而有更深入的理解。

什么是闭包

在MDN中给出闭包的概念是

JavaScript中的函数会形成闭包。 闭包是由函数以及创建该函数的词法环境组合而成。这个环境包含了这个闭包创建时所能访问的所有局部变量。

联系到《js执行上下文》这篇博客中提及函数执行上下文中的作用域链非常的相似。 查看一个简单的闭包例子

<ul>
    <ul id="list">
        <li>1</li>
        <li>2</li>
        <li>3</li>
        <li>4</li>
    </ul>
</ul>
var dom = document.querySelectorAll('#list li');
for (var i = 0, len = dom.length; i < len; i++){
    dom[i].onclick = function(){
        console.log(i);
    }
}

此时点击列表1234分别输出都是4,想要输出对应输出相同的序号则需要创建闭包

for (var i = 0, len = dom.length; i < len; i++){
        (function(i){
            dom[i].onclick = function(){
                console.log(i+1);
            }
        })(i)
    }

此时点击列表1234分别对应输出 1、2、3、4 相信大家也能够理解这个简单的例子,笔者从函数创建执行上下文的概念来解释下产生两种情况的原因。 在第一个例子的代码中 1、匿名函数创建的时候,确定了函数作用域(当前词法环境),当前词法环境是全局执行上下文的作用域,即对i的访问是全局作用域的访问。 2、for循环结束后,匿名函数分别绑定在对应的dom上,此时全局作用域的i为4。 3、触发列表的click事件,调用绑定的函数 4、函数调用,创建当前执行上下文,确定执行上下文的作用域链[VO,globalContext.VO] 5、执行代码console.log(i),在作用域链中查找i,找到全局作用域中的i为4 输出4.

在第二个例子的代码中 1、(function(){})()是IIFF(立即执行函数),在创建匿名函数后又立即执行。在该匿名函数(称作x)调用的时候,该函数执行上下文被创建。变量对象VO创建的过程中,参数i添加到变量对象VO。该函数作用域为[VO,globalContext.VO]。 2、dom[i].onclick = function(){..},匿名函数(称作y)创建,确定该函数作用域(当前词法环境),当前词法环境是x函数执行上下文的作用域。此时x函数执行上下文中i的值为对应传入参数的i的值 3、for循环结束后,dom 列表都绑定了对应的匿名函数,此时全局作用域的i为4 4、触发列表“1”的click事件,调用绑定的函数。 5、函数调用,创建当前执行上下文,确定执行上下文的作用域链[VO,xContext.VO] 6、执行代码console.log(i + 1),在作用域链中查找i,从顶端往下查找,找到xContext.VO中有变量i,其值为0,输出 i + 1为1。 7、点击其他列表同理。

通过对两个例子的解析,可以理解“闭包是由函数以及创建该函数的词法环境组合而成。”

闭包实际应用

1、闭包实现工厂函数

2、用闭包模拟私有方法 通过闭包的原理实现,私有变量方法的创建

var person = function(){    
    //变量作用域为函数内部,外部无法访问    
    var name = "default";       
    return {    
       getName : function(){    
           return name;    
       },    
       setName : function(newName){    
           name = newName;    
       }    
    }    
}();
print(person.name);//直接访问,结果为undefined    
print(person.getName());    
person.setName("abruzzi");    
print(person.getName());    

// 得到结果如下:  
// undefined  
// default  
// abruzzi

3、缓存数据

闭包的副作用

不合理使用闭包可能会造成内存高,JavaScript性能降低。

参考