JS 模块 p6
利用了闭包的模块:
简单模块例子:
function fn(){ var x = 1; function y(){ console.log(x); } return { y:y} }
var do1 = fn()
do1.y(); // 1
我们可以将这个“对象类型”的返回值看做是 模块的公共API
这个例子中返回的实例中 y() 等于是拥有了函数fn的内部作用域的闭包。
模块模式条件(书):
1.必须由外部的封闭函数,且被调用。
2.封闭函数必须返回至少一个内部函数。
单例模式(平时使用的比较多):
var single = (function(){ var x = 1; function fn(){ console.log(1); }; return { fn:fn } })(); single.fn();//1
将模块函数使用IIFE 表达出来,因为是立即调用,所以此实例的标识foo为单例。😵
还有一种在初始化html的时候使用的也比较多,如下:
(function(){ var u = {}; u.x = function(a){ console.log(a); } window.$custom = u; })(); $custom.x(1);//1
使用IIFE 立即给window动态增加一个属性,$custom,因为window的变量和方法是可以隐去的,我们可以直接调用$custom实例引用的方法x,最终打印1。
动态修改(返回实例引用中fn与fn2都用于内部作用的闭包,所以可以在外部通过调用fn2,动态修改内部id的值):
var foo = (function(){ var id = "hhh"; function fn(){ console.log(id); } function fn2(){ id = "MM" } return { fn:fn ,fn2:fn2 } })(); foo.fn();//hh foo.fn2(); foo.fn();//MM
当然了,你可以更改函数或者增加新的实例方法(之前的文章中有写 JS 创建自定义对象的方式方法)
再来一个(引用书中的例子):
var foo = (function x(){ var module = {}; function def(name,arr,impl){ for(let i = 0;i<arr.length;i++){ // 重新定义数组中的对象 arr[i] = module[arr[i]]; } module[name] = impl.apply(impl,arr); } function get(n){ return module[n]; } return {def:def,get:get} })()
定义一个先:
// 返回一个模块对象实例 foo.def('test',[],function(){
function h(n){return '我是test内部的h函数'+n} return { h : h }; }); foo.get('test').h('hi'); // 我是test内部的h函数hi // 返回方法函数本身 foo.def('test',[],function(){ function h(n){return '我是test内部的h函数'+n} return { h : h }; }); foo.get('test')('hi'); // 我是test内部的h函数hi
两种方式,第一种内部module的get 返回一个对象实例,含有h内部函数,可以调用;
第二种返回h函数本身,通过 '()' 直接调用即可。
再来一个,多重调用(在上面给module定义了test后,再来一个并且利用回调函数调用test)
foo.def('test2',['test'],function(callback){ function j(){ console.log(callback.h('hihihihi')+'其实我是test2'); } return { j:j } });
foo.get('test2').j();//我是test内部的h函数hihihihi其实我是test2
我们来分析一下,在foo.def 方法中核心的时候这句 apply(有关apply 、call和blind方面以后再表),简单说一下,apply方法把作用域第一个参数的作用域绑定或追加到调用的对象中,从何绑定this,并执行改方法,第二个参数就是执行这个方法需要的参数。
那么我们在调用def时,就是给foo中的内部对象module增加了一对键值对,module.name = impl() 这里得到impl函数执行后返回值
在调用get的时候,得到这个值,在第一个def中是test返回是 {h:h} 对象实例(调用foo.get()),所以我们使用 foo.get('test').h() 就调用了返回实例中的h函数,而h函数拥有访问h所在的作用域的闭包。
再来看第二个def,test2:
第二个def时我们给到3个参数,test,[test2]和一个匿名函数
foo在执行def方法时,首先循环数组,并重新对数组中的值进行重新赋值,这个例子中是 module[test],等于是得到了def1中的{h:h} 对象实例,然后调用apply方法,执行定义的匿名函数,返回值为 {j:j}
并且j方法中的callback 其实就是{h,h} ,(是这样的 module[test2] = impl.apply(impl,{h:h}) ),明白没?
所以foo中的内部对象 module又获得了一对简直,module.test2 = { j:j } ,并且j中的callback 是 {h:h}
最后我们通过 foo.get('test2') 获得了{j:j} 然后执行 j() 方法,并成功打印 “我是test内部的h函数hihihihi其实我是test2”。
此处在test2中,j()方法拥有作用域的闭包,也就是说可以访问到callback,而callback实际上在此事{h:h},而h也拥有其作用域的闭包。
可以说,这就是一种模块的表现方式或者说闭包的应用。
关于es6对模块有了重新的定义,再次我简单记录一下:
// test.js function h(){ return 'ggggg' } export h; // test2.js import h from 'test'; function j(){ console.log( h(); ) } export j; // foo.js module test from 'test'; module test2 from 'test2'; console.log( test2.h(); ) test2.j();
关键字:
import: 将一个模块的一个或者多个API 导入到当前作用域
export: 将当前的一个标识符导出为API
module: 将模块的API 导入并绑定到一个变量。