一、什么是闭包
函数在定义时的 词法作用域 以外的地方被调用,就会产生闭包。
二、产生闭包的原因
都是因为 词法作用域 造成的。无论通过何种手段将内部函数传递到所在的词法作用域以外,它都会持有对原始定义作用域的引用,无论在何处执行这个函数都会使用闭包,且使得该作用域能够一直存活,没办法进行垃圾回收。
function foo(){ let a = 1; function bar(){ console.log(a); } return bar; } const f = foo(); //这就是闭包(原本bar()是存在foo()作用域里的,外部不能返回,但现在foo()外部也能访问) f(); //1 for(var i=0;i<5;i++){ //与我们设想结果不一样的原因: 所有的回调函数依然是在循环结束后才会被执行,因此会每次输出一个5 出来。 setTimeout(()=> console.log(i),0); //5 5 5 5 5 } //所以,上面的代码等价于 for(var i=0;i<5;i++){} setTimeout(()=> console.log(i),0); //5 setTimeout(()=> console.log(i),0); //5 setTimeout(()=> console.log(i),0); //5 setTimeout(()=> console.log(i),0); //5 setTimeout(()=> console.log(i),0); //5 //解决方法1:运用IIFE创建闭包作用域 for(var i=0;i<5;i++){ (function(j){ setTimeout(()=> console.log(j),0); //0 1 2 3 4 })(i); } //解决方法2:使用let,生成块作用域 for(let i=0;i<5;i++){ setTimeout(()=> console.log(i),0); //0 1 2 3 4 }
三、应用
1、在定时器、事件监听器、Ajax请求、跨窗口通信、Web Workers或者任何其他的异步(或者同步)任务中,只要使 用了回调函数,实际上就是在使用闭包!
2、模块
function foo(){ let a = 1; function bar(){ console.log('foo bar'); } return {a, bar}; //返回含有对内部函数的引用的对象 } //创建模块实例 const f = foo(); f.bar(); //foo bar 这就是闭包
//模块管理器的原理(单例模式) 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 }; })(); MyModules.define('bar',[],function(){ function hello(who){ return "Let me instroduce " + 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 instroduce hippo foo.awesome(); //LET ME INSTRODUCE HIPPO
ES6中的模块(一个文件,一个模块)
//bar.js源码 function hello(who){ return "Let me instroduce " + who; } export {hello}; //foo.js源码 import {hello} from "./bar.js"; let hungry = "hippo"; function awesome(){ console.log(hello(hungry).toUpperCase()); } export {awesome}; html.html源码 <html> <head></head> <body> <script type="module"> import {awesome} from "./js/foo.js"; import {hello} from "./js/bar.js"; console.log(hello("rhino")); //Let me instroduce rhino awesome(); //LET ME INSTRODUCE HIPPO </script> </body> </html>