先来看一段代码
function foo() {
var something = "cool";
var another = [1,2,3];
function doSomething() {
console.log(something)
}
function doAnother() {
console.log(another.join(","))
}
}
foo函数包含了两个变量和两个函数,这里没有明显的闭包。这两个内部函数的词法作用域就是函数foo的内部作用域。接下来,我们把上边函数改进一下:
function foo() {
var something = "cool";
var another = [1,2,3];
function doSomething() {
console.log(something)
}
function doAnother() {
console.log(another.join(","))
}
return {doSomething, doAnother}
}
var fooInstance = foo();
fooInstance.doSomething();
fooInstance.doAnother();
这个模式在javascript中被称为模块,最常见的实现模块的方法通常被称为模块暴露。 这里foo只是一个函数,必须要调用它来创建一个模块实例,如果不执行外部函数,内部作用域和闭包都无法被创建。
因为foo函数每被调用一次就会创建一个模块实例。当只需要一个实例是,可以使用IIFE
来实现单例
var foo = (function foo() {
var something = "cool";
var another = [1,2,3];
function doSomething() {
console.log(something)
}
function doAnother() {
console.log(another.join(","))
}
return {doSomething, doAnother}
})()
现代的模块机制
大多数模块依赖加载器本质上都是将这种模块定义封装进一个友好的API。核心概念大抵如此:
var myModules = (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, get}
})()
myModules.define("bar", [], function(){
function hello(a){
return `Hello World Hello ${a}`
}
return {hello}
})
myModules.define("foo", ["bar"], function(bar) {
let person = "小明";
function awesome(){
return bar.hello(person).toUpperCase()
}
return {awesome}
})
var bar = myModules.get("bar");
var foo = myModules.get("foo");
console.log(bar.hello("小明")); //Hello World Hello 小明
console.log(foo.awesome()); //HELLO WORLD HELLO 小明
这就是模块管理器
的核心逻辑,“foo”和“bar”模块都是通过一个返回公共API的函数来定义的。“foo”甚至接收“bar”的实例作为依赖参数。
在ES6中,已经为模块增加了一级语法支持,但了解以前的模块管理器还是很有必要的,它使我们能够深入的理解闭包
模块有两个主要特征: ⑴. 为创建内部作用域而调用了一个包装函数;⑵. 包装函数的返回值必须至少包括一个对内部函数的引用,这样就会创建涵盖整个包装函数内部作用域的闭包。
网友评论