ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
# 简介 当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行。 <br> 如果将函数(访问它们各自的词法作用域)当作第一级的值类型并到处传递,你就会看到闭包在这些函数中的应用。在定时器、事件监听器、Ajax 请求、跨窗口通信、Web Workers 或者任何其他的异步(或者同步)任务中,只要使用了回调函数,实际上就是在使用闭包! <br> 闭包的例子: ~~~ function fn1() { var name = 'iceman'; function fn2() { console.log(name); } return fn2; } var fn3 = fn1(); fn3(); ~~~ 这样就清晰地展示了闭包: * fn2的词法作用域能访问fn1的作用域 * 将fn2当做一个值返回 * fn1执行后,将fn2的引用赋值给fn3 * 执行fn3,输出了变量name <br> 我们知道通过引用的关系,fn3就是fn2函数本身。执行fn3能正常输出name,这不就是fn2能记住并访问它所在的词法作用域,而且fn2函数的运行还是在当前词法作用域之外了。 <br> 正常来说,当fn1函数执行完毕之后,其作用域是会被销毁的,然后垃圾回收器会释放那段内存空间。而闭包却很神奇的将fn1的作用域存活了下来,**fn2依然持有该作用域的引用,这个引用就是闭包**。 <br> 注意:对函数值的传递可以通过其他的方式,并不一定值有返回该函数这一条路,比如可以用回调函数: ~~~ function fn1() { var name = 'iceman'; function fn2() { console.log(name); } fn3(fn2); } function fn3(fn) { fn(); } fn1(); ~~~ <br> 总结:**某个函数在定义时的词法作用域之外的地方被调用,闭包可以使该函数极限访问定义时的词法作用域**。 <br> <br> # 闭包的应用 ## 实现模块化 ~~~ var MyModules = (function Manager() { var modules = {}; function define(name, deps, impl) { for (var i = 0; i < deps.length; i++) { deps[i] = modules[deps[i]]; } modules[name] = impl.apply(impl, deps); } function get(name) { return modules[name]; } return { define: define, get: get }; })(); ~~~ <br> 调用 ~~~ MyModules.define("bar", [], function () { function hello(who) { return "Let me introduce: " + who; } return { hello: hello }; }); MyModules.define("foo", ["bar"], function (bar) { var hungry = "hippo"; function awesome() { console.log(bar.hello(hungry).toUpperCase()); } return { awesome: awesome }; }); var bar = MyModules.get("bar"); var foo = MyModules.get("foo"); console.log( bar.hello("hippo") ); // Let me introduce: hippo foo.awesome(); // LET ME INTRODUCE: HIPPO ~~~ <br> ## 循环调用 ~~~ for (var i=1; i<=5; i++) { (function() { var j = i; setTimeout( function timer() { console.log( j ); }, j*1000 ); })(); } ~~~ 改进 ~~~ for (var i=1; i<=5; i++) { (function(j) { setTimeout( function timer() { console.log( j ); }, j*1000 ); })( i ); } ~~~ <br> <br> # 参考资料 你不知道的JavaScript(上卷)