闭包:函数能够记住所在词法做用域,就产生了闭包,即便函数在当前词法做用域以外执行 ,闭包无处不在。。。请忽略这句话 ,看完在说javascript
function foo() { var a = 1; function bibao() { console.log(a); } return bibao; } var baz = foo(); baz();
bibao()能访问foo的内部做用域,而后bibao()自己做为值传递给baz,在foo执行以后,注意 baz=foo(),这里foo()会先运行,而后将返回值赋值给baz,而后运行baz(),实际是经过不一样的标识符引用内部的函数bibao();bibao()能够正常的执行,实现了bibao()在本身定义时的词法做用域之外执行。foo执行完成以后,一般期待foo()整个内部空间被销毁,被垃圾回收器回收空间,可是,拜baz()所赐,baz能访问foo()做用域,使得该做用域一直纯在。java
这个例子中,bibao对做用域的引用就叫闭包闭包
再来看一个传递函数是间接的:函数
var fn; function foo() { var a=2; function bibao() { console.log(a) } fn=bibao; } function bar() { fn();
}
foo();
bar();
同上面例子,不过这里外部函数运行,实现了全局变量fn对bibao()的引用,bibao()能访问foo内部词法做用域,在运行fn()的时候 ,就能实现了闭包spa
再好比:code
function foo() { var a = 2; function baz() { console.log( a ); // 2 } bar( baz ); } function bar(fn) { fn(); // 闭包 }
运行foo()实现baz的引用绑定到bar做用域 ,bar是全局函数对象
总结:blog
不管经过何种手段将内部函数传递到所在的词法做用域之外,它都会持有对原始定义做用
域的引用,不管在何处执行这个函数都会使用闭包。接口
在好比咱们常常写的函数:事件
function wait(message) { setTimeout( function timer() { console.log( message ); }, 1000 ); } wait( "Hello, closure!" );
这也是闭包
将一个内部函数(名为timer)传递给setTimeout(..)。timer 具备涵盖wait(..) 做用域的闭包,所以还保有对变量message 的引用。
wait(..) 执行1000 毫秒后,它的内部做用域并不会消失,timer 函数依然保有wait(..)做用域的闭包(也就是引用)。
function setupBot(name, selector) { $( selector ).click( function activator() { console.log( "Activating: " + name ); } ); } setupBot( "Closure Bot 1", "#bot_1" ); setupBot( "Closure Bot 2", "#bot_2" );
同理:事件函数也是闭包,本质上只要用到回调函数的都是使用闭包
在好比:
for (var i=1; i<=5; i++) { setTimeout( function timer() { console.log( i ); //结果是输出5次6 }, i*1000 ); }
显而易见的,延迟函数的回调会在循环结束时才执行,为何会捕捉不到?
它们被封闭在一个共享的全局做用域中,所以实际上只有一个i
解决办法:延迟函数的回调重复定义五次,彻底不使用循环,用匿名函数当即执行产生5个数做用域包裹对应变量i
for (var i=1; i<=5; i++) { (function() { setTimeout( function timer() { console.log( i ); }, i*1000 ); })(); }
固然,你也能够直接用ES新特性:
for (let i=1; i<=5; i++) { setTimeout( function timer() { console.log( i ); }, i*1000 ); }
延迟函数的回调能够将新的做用域封闭在每一个迭代内部,每一个迭代中都会含有一个具备正确值的变量供咱们访问
在好比:模块的例子,一个函数返回一个公用的对象接口
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.doSomething(); // cool foo.doAnother(); // 1 ! 2 ! 3
同理,调用CoolModule();实现了内部函数引用传递给外部词法做用域 ,foo就能访问CoolModule内部
1. 必须有外部的封闭函数,该函数必须至少被调用一次(每次调用都会建立一个新的模块
实例)。
2. 封闭函数必须返回至少一个内部函数,这样内部函数才能在私有做用域中造成闭包,并
且能够访问或者修改私有的状态。
实现单例模式
var foo = (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 }; })();
大量代码例子出自,原谅我剽窃,实在是让人豁然开朗
《Javascript 权威指南》
《你不知道的javascript》