怎样理解
一个函数记住并访问所在的词法作用域,即使函数在词法作用域之外执行,也能够访问涵盖的函数的内部作用域,这就形成了闭包。
1 2 3 4 5 6 7
| function foo(a){ return function bar(){ console.log(a); } } var bar=foo(2); bar();
|
无论经过多少标识符引用调用(将函数类型的值传递出去),bar()始终能涵盖foo()的内部作用域,所以在foo()词法作用域外部,也能访问变量a。
在定时器中也是如此
1 2 3 4 5 6
| function wait(msg){ setTimeout(function timer(){ console.log(msg); },1000); } wait('hello');
|
在定时器等待的1000ms中,wait的内部作用域并没有消失,timer函数依旧有涵盖了wait()内部作用域的闭包。
无论什么时候引擎调用这个函数(此处是timer),它的词法作用域都是完整的。
循环
举个常用例子,一个div里有四个button,加上点击事件
1 2 3 4 5
| for(var i=0;i<aBtn.length;i++){ aBtn[i].onclick=function(){ console.log(i); } }
|
众所周知,无论点哪个按钮,输出的都是4,它们都是被封闭在一个共享的全局作用域中,实际上只有一个i。
我们来修改一下形成闭包
1 2 3 4 5 6 7
| for(var i=0;i<aBtn.length;i++){ (function(i){ aBtn[i].onclick=function(){ console.log(i); } })(i); }
|
每次迭代一份function,形成新的作用域。可以将新的作用域封闭在迭代内部,每个迭代都会含有一个具有正确值的变量给我们访问。
模块
必要条件:
1.必须有外部的封闭函数,该函数至少被调用一次。
2.封闭函数必须返回至少一个内部函数,这样内部函数才能在私有作用域中产生闭包,并可以访问或者修改私有的状态。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| var foo=(function(con){ function change(){ public.foo1=foo2; } function foo1(){ console.log(con); } function foo2(){ console.log('abc'+con) } var public={ foo1:foo1, change:change }; return public; })("hello"); foo.foo1(); foo.change(); foo.foo1();
|
通过在模块实例内部保留对公共API对象的内部引用,可以从内部对模块进行修改,包括添加或删除方法和属性,以及修改它们的值。