JavaScript中闭包无处不在,只是我们能不能去识别它。
当函数可以记住并能访问所在的语法作用域时,就产生了闭包。我们来看下面这个例子
1.
function fun(){
var a=1;
function foo(){
console.log(num) 1
}
foo(){
}
fun()
这是闭包吗?
技术上来讲,也许是。我认为最准确地用来解释
foo(){对 num 的引用的方法是词法作用域的查找规则,而这些规则只是闭包的一部分。
2.下面我们来看一段代码,清晰地展示了闭包:
function foo() {
var a = 1;
function bar() {
console.log( a );
}
return bar;
}
var baz = foo();
baz(); // 1 —— 朋友,这就是闭包的效果。
函数 bar() 的词法作用域能够访问 foo() 的内部作用域。然后我们将 bar() 函数本身当作
一个值类型进行传递。在这个例子中,我们将 bar 所引用的函数对象本身当作返回值。
function foo() {
var a = 1;
function baz() {
console.log( a ); // 1
}
bar( baz );
}
function bar(fn) {
fn(); // 妈妈快看呀,这就是闭包!
}
把内部函数 baz 传递给 bar ,当调用这个内部函数时(现在叫作 fn ),它涵盖的 foo() 内部。作用域的闭包就可以观察到了,因为它能够访问 a 。
4再来看一个例子
function wait(message) {
setTimeout( function timer() {
console.log( message );
}, 200 );
}
wait( "Hello, world!" );
将一个内部函数(名为 timer )传递给 setTimeout(…) 。 timer 具有涵盖 wait(…) 作用域
的闭包,因此还保有对变量 message 的引用。
wait(…) 执行 1000 毫秒后,它的内部作用域并不会消失, timer 函数依然保有 wait(…)
作用域的闭包。
深入到引擎的内部原理中,内置的工具函数 setTimeout(…) 持有对一个参数的引用,这个
参数也许叫作 fn 或者 func ,或者其他类似的名字。引擎会调用这个函数,在例子中就是
内部的 timer 函数,而词法作用域在这个过程中保持完整。
这就是闭包。
5.如果你很熟悉 jQuery(或者其他能说明这个问题的 JavaScript 框架),可以思考下面
的代码:
function setupBot(name, selector) {
$( selector ).click( function activator() {
console.log( "Activating: " + name );
} );
}
setupBot( "Closure Bot 1", "#bot_1" );
setupBot( "Closure Bot 2", "#bot_2" );
总结:在定时器、事件监听器、Ajax 请求、跨窗口通信、Web Workers 或者任何其他的异步(或者同步)任务中,只要使用了回调函数,实际上就是在使用闭包!