利用闭包的强大威力,但从表面上看,它们彷佛与回调无关。下面一块儿来研究其中最强大的一个:模块。闭包
1 function foo() { 2 var something = "cool"; 3 var another = [1, 2, 3]; 4 function doSomething() { 5 console.log( something ); 6 } 7 function doAnother() { 8 console.log( another.join( " ! " ) ); 9 } 10 }
正如在这段代码中所看到的,这里并无明显的闭包,只有两个私有数据变量something和another,以及doSomething() 和doAnother() 两个内部函数,它们的词法做用域(而这就是闭包)也就是foo() 的内部做用域。
接下来考虑如下代码:ide
1 function CoolModule() { 2 var something = "cool"; 3 var another = [1, 2, 3]; 4 5 function doSomething() { 6 alert( something ); 7 } 8 function doAnother() { 9 alert( another.join( " ! " ) ); 10 } 11 return { 12 doSomething: doSomething, 13 doAnother: doAnother 14 }; 15 } 16 var foo = CoolModule(); 17 foo.doSomething(); // cool 18 foo.doAnother(); // 1 ! 2 ! 3
这个模式在JavaScript 中被称为模块。最多见的实现模块模式的方法一般被称为模块暴露,这里展现的是其变体。咱们仔细研究一下这些代码。函数
首先,CoolModule() 只是一个函数,必需要经过调用它来建立一个模块实例。若是不执行外部函数,内部做用域和闭包都没法被建立。其次,CoolModule() 返回一个用对象字面量语法{ key: value, ... } 来表示的对象。这个返回的对象中含有对内部函数而不是内部数据变量的引用。咱们保持内部数据变量是隐藏且私有的状态。能够将这个对象类型的返回值看做本质上是模块的公共API。这个对象类型的返回值最终被赋值给外部的变量foo,而后就能够经过它来访问API 中的属性方法,好比foo.doSomething()。spa
从模块中返回一个实际的对象并非必须的,也能够直接返回一个内部函
数。jQuery 就是一个很好的例子。jQuery 和$ 标识符就是jQuery 模块的公
共API,但它们自己都是函数(因为函数也是对象,它们自己也能够拥有属
性)。code
doSomething() 和doAnother() 函数具备涵盖模块实例内部做用域的闭包( 经过调用CoolModule() 实现)。当经过返回一个含有属性引用的对象的方式来将函数传递到词法做用域外部时,咱们已经创造了能够观察和实践闭包的条件。若是要更简单的描述,模块模式须要具有两个必要条件。对象
1. 必须有外部的封闭函数,该函数必须至少被调用一次(每次调用都会建立一个新的模块实例)。
2. 封闭函数必须返回至少一个内部函数,这样内部函数才能在私有做用域中造成闭包,而且能够访问或者修改私有的状态。
一个具备函数属性的对象自己并非真正的模块。从方便观察的角度看,一个从函数调用所返回的,只有数据属性而没有闭包函数的对象并非真正的模块。上一个示例代码中有一个叫做CoolModule() 的独立的模块建立器,能够被调用任意屡次,每次调用都会建立一个新的模块实例。当只须要一个实例时,能够对这个模式进行简单的改进来实现单例模式:blog
1 var foo = (function CoolModule() { 2 var something = "cool"; 3 var another = [1, 2, 3]; 4 5 function doSomething() { 6 alert( something ); 7 } 8 function doAnother() { 9 alert( another.join( " ! " ) ); 10 } 11 return { 12 doSomething: doSomething, 13 doAnother: doAnother 14 }; 15 })(); 16 foo.doSomething(); // cool 17 foo.doAnother(); // 1 ! 2 ! 3
当即调用这个函数并将返回值直接赋值给单例的模块实例标识符foo。
模块也是普通的函数,所以能够接受参数:ip
1 function CoolModule(id) { 2 function identify() { 3 console.log( id ); 4 } 5 return { 6 identify: identify 7 }; 8 } 9 var foo1 = CoolModule( "foo 1" ); 10 var foo2 = CoolModule( "foo 2" ); 11 foo1.identify(); // "foo 1" 12 foo2.identify(); // "foo 2"
模块模式另外一个简单但强大的变化用法是,命名将要做为公共API 返回的对象:作用域
1 var foo = (function CoolModule(id) { 2 function change() { 3 // 修改公共API 4 publicAPI.identify = identify2; 5 } 6 function identify1() { 7 alert( id ); 8 } 9 function identify2() { 10 alert( id.toUpperCase() ); 11 } 12 var publicAPI = { 13 change: change, 14 identify: identify1 15 }; 16 return publicAPI; 17 })( "foo module" ); 18 foo.identify(); // foo module 19 foo.change(); 20 foo.identify(); // FOO MODULE
经过在模块实例的内部保留对公共API 对象的内部引用,能够从内部对模块实例进行修改,包括添加或删除方法和属性,以及修改它们的值。io