闭包的总结(干货1)
闭包:函数可以记住所在词法作用域,就产生了闭包,即使函数在当前词法作用域之外执行 ,闭包无处不在。。。请忽略这句话 ,看完在说
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()作用域,使得该作用域一直纯在。
这个例子中,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()的时候 ,就能实现了闭包
再比如:
function foo() { var a = 2; function baz() { console.log( a ); // 2 } bar( baz ); } function bar(fn) { fn(); // 闭包 }
运行foo()实现baz的引用绑定到bar作用域 ,bar是全局函数
总结:
无论通过何种手段将内部函数传递到所在的词法作用域以外,它都会持有对原始定义作用
域的引用,无论在何处执行这个函数都会使用闭包。
在比如我们经常写的函数:
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》