You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
MyModules.define("bar",[],function(){functionhello(who){return"Let me introduce:"+who;}return{hello:hello};});MyModules.define("foo",["bar"],function(bar)){// "foo"和"bar"模块都是通过一个返回公共API的函数来定义的,"foo"甚至还接受"bar"的实例作为依赖参数,并能相应地使用它varhungry='hippo';functionawesome(){console.log(bar.hello(hungry).toUpperCase());}return{awesome:awesoem};});varbar=MyModules.get("bar");varfoo=MyModules.get("foo");console.log(bar.hello("hippo"));// Let me introduce:hippofoo.awesome();// LET ME INTRODUCE:HIPPO
// bar.jsfunctionhello(who){return"Let me introduce:"+who;}exporthello;// export:导出功能:公开在模块中声明的内容,并让其它模块加以使用// foo.js// 仅从"bar"模块导入hello()importhellofrom"bar";varhungry="hippo";functionawesome(){console.log(bar.hello(hungry).toUpperCase());}exportawesome;// baz.js// 导入完整的"foo"和"bar"模块modulefoofrom"foo";modulebarfrom"bar";console.log(bar.hello("thino"));// Let me introduce:thinofoo.awesome();//LET ME INTRODUCE:THINO
作用域闭包
当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行的。
什么是闭包呢?
bar()依然持有对该作用域的引用,这个引用就叫做“闭包”。
无论使用何种方式对函数类型的值进行传递,当函数在别处被调用的时候都可以观察到闭包:
传递函数也可以是间接的:
上面的代码把一个内部函数timer传递给setTimeout()。timer具有涵盖wait()作用域的闭包,因此还保持有对变量message的引用。
wait()执行1000毫秒后,它的内部作用域并不会消失,timer函数依然保持有wait()作用域的闭包。
在引擎内部,内置的工具函数setTimeout()持有对一个参数的引用,这个参数可能叫做fn或者func,或者其他类似的名字。引擎会调用这个函数,在例子中就是内部的timer函数,而词法作用域在这个过程中保持完整。
这就是闭包。
我们尝试假设循环中每个迭代在运行时都会给自己“捕获”一个i副本。但是根据作用域的工作原理,实际上是尽管循环中的五个函数是在各个迭代中分别定义的,但是它们都被封闭在一个共享的全局作用域中,因此实际上只有一个i。
在迭代内使用IIFE会为每一个迭代都生成一个新的作用域,使得延迟函数的回调可以将新的作用域封闭在每个迭代内部,每个迭代中都会含有一个具有正确值的变量供我们访问。
块作用域+闭包
我们使用IIFE在每次迭代时都创建一个新的词法作用域,也就是说,每次迭代都需要一个块级作用域。
本质上,这是将一个块转换成一个可以被关闭的作用域。
模块
上面这段代码,没有明显的闭包,只有两个私有数据变量something和another,以及doSomething()和doAnother()两个内部函数,它们的词法作用域(而这就是闭包)也就是foo()的内部作用域。
请看下面代码:
这个模式在javascript中被称为模块。最常见的实现模块模式的方法通常被称为模块暴露,这里是其变体。
首先,CoolModule()只是一个函数,必须通过调用它来创建一个模块实例,如果不执行外部函数,内部作用域和闭包都无法被创建。
其次,CoolModule()返回一个用对象字面量语法{ key:value,... }来表示的对象,这个返回的对象中含有对内部函数而不是内部数据变量的引用,我们保持内部数据变量是隐藏且私有的状态。可以把这个对象类型的返回值看作本质上是模块的公共API。
这个对象类型的返回值最终被赋值给外部的变量foo,然后就可以通过它来访问API中的属性方法,比如foo.soSomething()。
doSomething()和doAnother()函数具有涵盖模块实例内部作用域的闭包(通过调用CoolModule()实现)。
当通过返回一个含有属性引用的对象的方式的对象的方式来将函数传递到词法作用域外部时,我们已经创造了可以观察和实践闭包的条件。
上一个实例代码中有一个叫作CoolModule()的独立模块创建器,可以被调用任意多次,每次调用都会创建一个新的模块实例。当只需要一个实例的时候,可以像如下代码那样对这个模式改进:
模块也是普通的函数,所以可以接受参数:
模块模式另一个强大的用法是命名将要作为公共API返回的对象:
通过在模块实例的内部保留对公共API对象的内部引用,可以从内部对模块实例进行修改,包括添加或者删除属性和方法,以及修改它们的值。
现在的模块机制
大多数模块依赖加载器/管理器本质上都是将这个模块定义封装进一个友好API:
下面展示如何使用它来定义模块:
它们符合模块模式的两个特点:调用包装了函数定义的包装函数,并且将返回值作为该模块的API。
ES6中的模块机制
在通过模块系统进行加载时,ES6会将文件当作独立的模块来处理。每个模块都可以导入其他模块或特定的API成员,同样也可以导出自己的API成员。
ES6的模块没有“行内”格式,必须被定义在独立的文件中(一个文件一个模块)。浏览器或引擎有一个默认的“模块加载器”可以在导入模块的同时同步地加载模块文件。
模块文件中的内容会被当作好像包含在作用域闭包中一样来处理,就和前面介绍的函数闭包模块一样。
The text was updated successfully, but these errors were encountered: