1.闭包,一目了然当我接触到一项新技术时,我首先要做的事情之一就是找到它的demo。对我们来说,看代码比自然语言更能理解一个事物的本质。事实上,闭包无处不在。比如jQuery和zepto的核心代码都包含在一个大的闭包里面,那我就先写最简单最原始的闭包,让大家脑补一下闭包。屏幕:functionA(){functionB(){console.log("HelloClosure!");}returnB;}varC=A();C();//HelloClosure!这是最简单的闭包。有了初步的了解之后,我们来简单分析一下它和普通函数的区别。上述代码翻译成自然语言如下:(1)定义普通函数A(2)在A中定义普通函数B(3)在A中返回B(4)执行A,并将A的返回结果赋值给变量C(5)执行C,把这五个步骤总结成一句话:函数A的内部函数B被函数A外的变量c引用。再处理这句话就变成了闭包的定义:当一个内部函数被其外部的变量引用时外部函数,形成一个闭包。因此,当你执行以上5个步骤时,你就定义了一个闭包!这是关闭。2.闭包的作用在了解闭包的作用之前,我们先来了解一下Javascript中的GC机制:在Javascript中,如果一个对象不再被引用,那么这个对象就会被GC回收,否则这个对象会一直存放在内存中.上面的例子中,B定义在A中,所以B依赖于A,而外部变量C引用了B,所以A被C间接引用了。换句话说,A不会被GC回收,会一直保留在记忆中。为了证明我们的推理,上面的例子稍微改进一下:functionA(){varcount=0;functionB(){count++;console.log(count);}returnB;}varC=A();C();//1C();//2C();//3count是函数A中的一个变量,在函数B中改变了它的值,函数B每执行一次,count的值就会在原来的基础上加1。因此,函数A中的count变量会一直保存在内存中。当我们需要在一个模块中定义一些变量,并希望这些变量一直保存在内存中而不“污染”全局变量时,我们可以使用闭包来定义这个模块。3、闭包的高级写法上面的写法其实是最原始的写法,但是在实际应用中,闭包和匿名函数会一起使用。下面是闭包的常用写法:(function(document){varviewport;varobj={init:function(id){viewport=document.querySelector("#"+id);},addChild:function(child){viewport.appendChild(child);},removeChild:function(child){viewport.removeChild(child);}}window.jView=obj;})(文档);这个组件的作用是:初始化一个容器,然后给这个容器添加子容器,也可以移除一个容器。函数很简单,但是这里涉及到另外一个概念:立即执行函数。简单了解一下就够了。关键要理解的是这种写法是如何实现闭包功能的。上面的代码可以拆成两部分:(function(){})和(),第一个()是一个表达式,表达式本身就是一个匿名函数,所以加()就是执行这个匿名函数。因此这段代码的执行过程可以分解为:varf=function(document){varviewport;varobj={init:function(id){viewport=document.querySelector("#"+id);},addChild:function(child){viewport.appendChild(child);},removeChild:function(child){viewport.removeChild(child);}}window.jView=obj;};f(document);好像看到了闭包在这段代码,但是f里面没有返回值,好像不满足关闭条件,注意这段代码:window.jView=obj;obj是函数f中定义的一个对象,在这个对象中定义了一系列的方法。执行window.jView=obj就是在window全局对象中定义一个变量jView,并将这个变量指向obj对象,即全局变量jView引用obj。而obj对象中的函数引用了函数f中的变量viewport,所以函数f中的viewport不会被GC回收,viewport会一直保存在内存中,所以这种写法满足了条件关闭。4.总结这是对闭包最简单的理解。当然,闭包也有更深层次的理解。这涉及到很多。你需要了解JS的执行上下文、激活对象,以及作用域(scope)和作用域链(scopechain)的运行机制。但是作为初学者,暂时不需要了解这些。有了简单的了解之后,就要在实际项目中使用了。当你用的多了,自然会对闭包有更深的理解!
