当函数能够记住并访问所在的词法做用域时,就产生了闭包,即便函数是在当前词法做用域以外执行,简单上讲,就是在一个函数中内部的函数。javascript
function foo() { var a = 2; function bar() { console.log( a ); } return bar; } var baz = foo(); baz(); // 2 —— 朋友,这就是闭包的效果。
那么bar函数就是一个闭包,它能够在做用域外被访问。
各类闭包实例:java
function foo() { var a = 2; function baz() { console.log( a ); // 2 } bar( baz ); } function bar(fn) { fn(); // 妈妈快看呀,这就是闭包! }
function setupBot(name, selector) { $( selector ).click( function activator() { console.log( "Activating: " + name ); } ); } setupBot( "Closure Bot 1", "#bot_1" ); setupBot( "Closure Bot 2", "#bot_2" );
function wait(message) { setTimeout( function timer() { console.log( message ); }, 1000 ); } wait( "Hello, closure!" );
本质上不管什么时候何地,若是将函数看成第一级的值类型并处处传递,就会由闭包,在定时器、事件监听器、Ajax请求、跨窗口通讯、Web Workers或者其余的异步(或者同步)任务中,只要使用了回调函数,实际上就是闭包闭包
模块实例:app
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
这种模式在JavaScript中就是模块。
最多见的实现模块模式的方法一般被称为模块暴露。
模块模式所具有的两个必要特色:异步
必须有外部的封闭函数,该函数至少被调用一次(每次调用都会产生新的模块实例)。 封闭函数必须返回至少一个内部函数,这样内部函数才能在私有做用域中造成闭包,而且能够访问或者修改私有的状态。
模块模式的另外一个简单但强大的用法是命名将要做为公共API返回的对象。ide
var foo = (function CoolModule(id) { function change() { // 修改公共 API publicAPI.identify = identify2; } function identify1() { console.log( id ); } function identify2() { console.log( id.toUpperCase() ); } var publicAPI = { change: change, identify: identify1 }; return publicAPI; })( "foo module" ); foo.identify(); // foo module foo.change(); foo.identify(); // FOO MODULE
经过在模块实例的内部保留对公共API对象的内部引用,能够从内部对模块实例进行修改,包括添加或删除方法和属性,以及修改他们的值。
现代的简单模块机制:函数
var MyModules = (function Manager() { var modules = {} function define(name, argus, func) { for (var i = 0; i < argus.length; i++) { argus[i] = modules[argus[i]] } modules[name] = func.apply(func,argus) } function get(name) { return modules[name] } return { define: define, get: get } })() MyModules.define("bar", [], function () { function hello(who) { return "Let me introduce: " + who; } return { hello: hello }; }); MyModules.define("foo", ["bar"], function (bar) { var hungry = "hippo"; function awesome() { console.log(bar.hello(hungry).toUpperCase()); } return { awesome: awesome }; }); var bar = MyModules.get("bar"); var foo = MyModules.get("foo"); console.log( bar.hello.call(undefined, "hippo") ); // Let me introduce: hippo foo.awesome.call(undefined); // LET ME INTRODUCE: HIPPO
"foo"和"bar"模块都是经过一个返回公共API的函数来定义的,“foo”甚至接受“bar”的实例做为依赖参数,并能相应的使用它。
它们符合前面列出的模块模式的两个特色:调用包装了函数定义的包装函数,而且将返回值做为该模块的API。code
ES6的模块机制
ES6中为模块增长了一级语法支持。在经过模块系统进行加载时,ES6会将文件看成独立的模块来处理。每一个模块均可以导入其余模块或特定的API成员,同时也能够导出自身的API成员。
重构以前的模块得三个文件对象
function hello(who) { return "Let me introduce: " + who } export hello
import hello from "bar" var hungry = "hippo" function awesome() { console.log(hello(hungry).toUpperCase()) } export awesome
module foo from "foo" module bar from "bar" console.log(bar.hello("hippo")) foo.awesome
import方法:将一个模块中的一个或多个API导入到当前做用域中,并绑定在一个变量上。
module方法:将整个模块的API导入并绑定到一个变量上
export方法:将当前模块的一个标识符导出为公共API
模块文件中的内容会被看成包含在做用域闭包中同样来处理。事件