闭包
闭包是指有权访问另一个函数作用域中的变量的函数(红皮书);
闭包是函数和声明该函数的词法环境的组合(MDN)。
当函数可以记住并访问所在的词法作用域时,就产生了闭包(你不知道的js)。
以上是翻阅的三个答案,其中都提到了作用域和访问函数作用域,这与红皮书的执行环境、作用域链有关,我有整理在随笔中。
function foo () {
var a = 2;
function bar () {
console.log(a);
}
return bar;
}
var baz = foo();
baz(); //2
上面的例子向我们展示了闭包的效果。bar()的词法作用域能够访问foo()的内部作用域,baz执行实际上只是通过不同的标识符调用了bar();
通常foo执行完后,foo的内部作用域会被销毁,但是闭包阻止了这件事情的发生,因为bar() 一直在使用它,使得该作用域能够一直存活,以供bar() 在之后任何时间进行引用。
bar()依然持有对该作用域的引用,而这个引用就叫作闭包。
例1:
function foo () {
var a = 2;
function baz () {
console.log(a);
}
bar(baz);
}
function bar(fn) {
fn();
}
foo();
bar();//2
例2:
var fn;
function foo () {
var a = 2;
function baz () {
console.log(a);
}
fn = baz;
}
function bar(fn) {
fn();
}
foo();
bar();//2
闭包的身影
无论何时何地,如果将函数当作第一级的值类型并到处传递,就会有闭包在这些函数中应用。在定时器、事件监听、Ajax请求、跨窗口通信、web Workers或者任何其他的异步(或者同步)任务中只要使用了回调函数,实际上就是在使用闭包。
循环与闭包
for ( var i = 0; i<5; i++ ){
(function(){
setTimeout(function timer() {
console.log(i);
},1000);
})();
}
// 5 ....
for ( var i = 0; i<5; i++ ){
(function(){
var j = i;
setTimeout(function timer() {
console.log(j);
},1000);
})();
}
// 0 1 2 3 4
循环1虽然把作用域封闭起来,但作用域是空的,所以并不能达到想要的目标,可见仅仅封闭是不够的,只有包含一点实际的变量才能为我们所用。
模块
function a () {
function b () {
}
function c () {
}
return {
b: b,
c: c
}
}
var d = a();
d.b();
d.c();
这个模式在JS中被称为模块。最常见的实现模块模式的方法通常被称为模块暴露,展示的是其变体。
现代模块机制
var Modules = (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
}
})();
Modules.define( "bar", [], function(){
funciton hello(who) {
return "Hello " + who;
}
return {
hello: hello
};
});
Modules.define("foo", ["bar"], function(bar){
var hungry = 'hippo';
function awesome() {
console.log( bar.hello( hungry ).toUpperCase() );
}
return {
awesome: awesome
}
});
var bar = Modules.get( "bar" );
var foo = Modules.get( "foo" );
console.log( bar.hello('world') );
foo.awesome();// Hello hippo