前提:JavaScript中闭包无处不在,你只须要可以识别并拥有它。闭包是基于词法做用域书写代码时天然产生的结果。闭包
简单的来讲词法做用域就是定义在词法阶段的做用域,换就话说,词法做用域是由你在写代码时将变量和块做用域写在哪里来决定的app
function foo(a){ var b = a*2; function bar(c){ console.log(a,b,c); } bar (b*3); } foo(2);
在这个例子中包含了三个逐级嵌套的做用域异步
关于词法做用域咱们就现讲这么多,接下来仍是回到咱们的正文,做用域闭包函数
function foo(){ var a=2; function bar(){//bar()的词法做用域可以访问foo()的内部做用域 console.log(a); } return bar;//将bar()函数当作一个值类型进行传递 } var baz =foo(); baz(2);
foo()内部做用域依然存在,没有被回收。bar()依然持有该做用域的引用。这个引用就叫闭包工具
function foo(){ var a=2; function baz(){ console.log(a); } bar(baz); } function bar(fn){ fn(); } foo(); //把内部函数baz传递给bar, // 当调用这个内部函数, // 他涵盖的foo()内部做用域的闭包就能够观察到了,由于它可以访问a
var fn; function foo(){ var a =2; function baz(){ console.log(a); }; fn = baz; } function bar(){ fn(); } foo(); bar();
function wait(message){ setTimeout(function timer(){ console.log(message) },1000); }; wait("hello world");
在引擎内部,内置的工具函数setTimeout()持有对一个参数的引用,引擎会调用这个函数,在这个例子中就是内部的timer函数,而词法做用域就在这个过程当中保持完整。这就是闭包。code
for(var i=0;i<=5;i++){ setTimeout(function timer() { console.log(i); }, i*1000); } //你们猜猜结果会是啥?
正常状况下会分别输出数字1~5,但实际会输出五次6。ip
代码中有什么缺陷致使它的行为通语义所暗示的不一致呢?
咱们须要更多的做用域,特别是在循环的过程当中每一个迭代都要一个闭包做用域,所以想到了当即执行函数作用域
for( var i=0;i<=5;i++){ (function(){ setTimeout(function timer() { console.log(i); }, i*1000); })(); }
这样子为何还不行呢?咱们显然拥有了更过的词法做用域。
每一个延迟函数都会讲IIFE在每次迭代中建立的做用域封闭起来。get
for( var i=1;i<=5;i++){ (function(){ var j =i; setTimeout(function timer() { console.log(j); }, j*1000); })(); }
for(let i =1;i<=5;i++){ setTimeout(function timer() { console.log(i); }, i*1000); }
let欺骗此法做用域,每次在迭代都去建立一个新的做用域,而后执行完后被销毁,这样每一个迭代都有本身的做用域就能够达到咱们的预期效果,输出1~5。it
function coolModule(){ var something = 'cool'; var another = [1,2,3]; function doSomething(){ console.log(something); } function doAnother(){ console.log(another.join('!')); } return { doSomething: doSomething, doAnother: doAnother }; } var foo = coolModule(); foo.doAnother(); foo.doSomething();
这个模式JavaScript中被称为模块,保护私有属性,只提供公共方法。
大多数模块依赖加载器/管理器本质上都是将这种模块定义封装进一个友好的API。
var MyModules = (function Manager(){ var modules = {}; function define(name,deps,impl){ for(var i=0;i<deps.length;i++){ deps[i] = module[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 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")); foo.awesome();