当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前作用域的外面执行。
function foo(){ var a = 2; function bar(){ console.log(a) }; return bar; } var baz = foo(); baz(); //2 这就是闭包
在这个例子中,bar可以正常执行,它是在直接定义的词法作用域外执行的。通常,foo执行完后JS引擎会执行垃圾回收机制,但由于函数bar作为返回值赋值给baz,所以bar不会被回收,而bar覆盖了foo的内部作用域,故foo不会被回收,此时通过执行baz可以正常引用foo内部的变量,这就是闭包。
闭包的作用:模块
一个小例子:
function foo(){ var a=1,b=2; function doSome (){ console.log(a); } function doOther(){ console.log(b) } return { doSome:doSome, doOther:doOther } } var baz = foo(); baz.doSome(); // 1 baz.doOther(); // 2
上面的例子中,foo的返回值是一个对象,对象的引用是内部的两个函数doSome和doOther,与上面同理,通过对函数的返回来实现随时访问foo内部的变量。
假设我们把foo定义为立即执行函数,则可以实现对内部变量a和b的私有保护,如下:
(function foo(){ var a=1,b=2; function doSome (){ console.log(a); } function doOther(){ console.log(b) } return { doSome:doSome, doOther:doOther } })(); foo.doSome(); // 1 foo.doOther(); // 2
对于模块,每一次都需要对函数foo执行后才能引用其返回的对象,注意:每一次返回的对象值都是不同的,因为每一次函数执行时都会创造一个新的作用域,而返回的对象中的函数所覆盖的就是这个作用域。(函数定义和函数执行的作用域不同,每次执行函数都是一个新的作用域)假如在实际开发中只需实例化一次foo,那我们可以采用单例模式,即上面的立即执行函数,假如想根据不同参数执行不同代码,可以采用如下模式的代码:
function foo(a){ function doSome(){ console.log(a) } return {doSome:doSome} }; var baz = foo(1); var bar = foo(2); baz.doSome(); //1 bar.doSome(); //2
现代的模块化机制代码示例(具体使用和解释参见上卷第五章):
var Mym = (function Manager(){ var moul ={}; function defined (name,deps,impl){ for(var i = 0;i<deps.length;i++) deps[i] = moul[deps[i]] } moul[name] = impl.apply(impl,deps); function get(name){ return moul[name] } return { defined:defined, get:get } })();