Module模式体现了js的封装性,把内部细节对外部隐藏,暴露出公共接口给调用者去调用。分为两种方式–字面量表示法和module模式,module是字面量的高级表现形式。
本文以module模式提出者的一篇文章 《A JavaScript Module Pattern》【我看不懂你们看着办】为蓝本,参考胖大叔版本的译文以及刘卿版本译文,对文内提及的各个方法进行更加细致的深入,每个例子均可运行在webkit内核的chrome中,其他浏览器未测。
对象字面量表示法
一个模块等于一个花括号{}包含的对象集合,每个对象以冒号分隔属性值、逗号结尾,否则出现错误。单一属性直接以逗号结尾;多属性以花括号{}包裹,采用键值对形式书写。
Error : Uncaught SyntaxError: Unexpected identifier
- 个人理解:
var Module = {
myProperty : "zjfirst",
myConfig : {
name : "552378416",
password :"zxc456."
},
myMethod : function(){
console.log("in love");
}
}
console.log(Module.myProperty);
console.log(Module.myConfig);
console.log(Module.myConfig.__proto__.__proto__ === null);
console.log("name : "+Module.myConfig.name);
console.log("password : "+ Module.myConfig.password);
Module.myMethod();
- webkit截图
Module模式
Module模块化设计主要是通过闭包的方式,封装私有属性和方法,暴露一个公有api给外界去调用。如果这个模块需要暴露出去给其他人用作为一个工具库的话,最好使用 UMD 模式,也就是判断是否支持 AMD、CommonJS模块加载,UMD 做的事就是兼容!兼容!兼容!
- 个人理解:
var Module = (function(){
var privatevar = 0 ;
var privateMethod = function(foo){
console.log(foo);
};
return{
publicvar : "U: i am hungry , want eat sb !",
publicMethod : function(bar) {
privateMethod(bar);
},
publicMethod2 : function(){
console.log("I: U r very hungry !");
}
};
}());
Module.publicMethod("I: Who is hungry ?");
Module.publicMethod2();
console.log(Module.publicvar);
console.log(Module.privatevar);
- 加入UMD
(function(root, factory){
if(typeof define === 'object' && define.amd) {
define([], factory);
} else if(typeof module === 'object' && module.exports) {
module.exports = factory();
} else {
root.ModuleName = factory();
}
}(this, function(){
var module = {};
module.publicMethod = ...;
return module;
}));
- webkit截图
–高级用法
多人合作,一个模块分割成多个文件的故事。
松耦合
任意顺序加载多个Module,如果不存在Module模型,{}赋值给var Module,并向其中添加新的元素属性,返回值为新的Module。”强内聚,弱耦合”,开发追求的标准莫不过如此。
- 代码结构:
var Module = (function (my) {
my.abc = "abc";
return my;
} (Module || {}));
- 个人理解:
var Module = Module || {}; //解决多文件加载时候module如何定义问题,有定义增加一些方法;无定义那就白手起家
var i, len, obj ,flag ;
Module = (function (my) {
my.add = function () {
var reg = /^(-?\d+)(\.\d+)?$/ ; //数字正则
len = arguments.length; //不定参数
flag = 0;
for(i = 0;i < len;i++){
if(reg.test(arguments[i])){
flag += 1;
}
}
if(flag == len){
obj = 0;
for(i = 0;i < len;i+=1){
obj += arguments[i];
}
}else{
obj = "";
for(i = 0;i < len;i+=1){
obj += arguments[i];
}
}
console.log(obj);
}
return my;
} (Module || {}));
Module.add(2, "aaaaa");
Module.add(2, "aaaaa", 121);
Module.add(2.2, 5.5,8.7,5.9);
webkit截图
紧耦合(未完待续)下班回家继续研究
…
…
…
22:00分 吃完饭洗完脸 坐在电脑前 折腾自己好久没开机的电脑 安装好常用的软件、插件之后 继续研究没研究完的东西….
…
…
…
紧耦合加强之间的模块之间联系,减少单独模块的复用性,获得的好处是有加载顺序,可以安全的重载方法。
- 代码结构:
var MODULE = (function (my) {
var old_moduleMethod = my.moduleMethod;
my.moduleMethod = function () {
// method override, has access to old through old_moduleMethod...
};
return my;
}(MODULE));
- 个人理解:
var Module = Module || {};
Module = (function(my){
my.addPhoto = function (foo) {
console.log(foo);
};
return my;
}(Module));
Module = (function (my) {
var add = my.addPhoto; //紧耦合的关键点
my.addPhoto = function(){
add("i am a sb !!");
console.log("i love me !!");
}
return my;
}(Module));
Module.addPhoto();
- webkit截图
克隆与继承(未完待续)夜深 程序员去挺尸了…
…
…
…
上班一小时,脑子活跃了,开始整理我的学习心得,公司前端大佬们在上演“人月神话”的故事。作为一只进击的小白,只好继续坚持(偷偷发育,我们能赢)的策略,等大佬争论结束后,默默给大佬打打杂。
…
…
…
关于这个知识点,我纠结再三还是将克隆与继承糅合在一起去介绍,可能代码有点绕,大家多花一丢丢时间就可以弄明白了。
克隆这里介绍的是浅拷贝,理解这一小节需要掌握的知识包含:深度克隆、原型链、继承、for-in loops 以及上面的铺垫。所谓克隆就是复制原有模块方法为自己所用(js中方法的传递采用值传递的特殊方式–引用传递),加上重载的函数,感觉比以前要叼许多了。按照惯例,给大家上翻译过后的module关于clone与inheritance的结构:(刘卿的版本更有原汁原味)
- 代码结构:
var MODULE_TWO = (function (old) {
var my = {},
key;
for (key in old) {
if (old.hasOwnProperty(key)) {
my[key] = old[key];
}
}
var super_moduleMethod = old.moduleMethod;
my.moduleMethod = function () {
// override method on the clone, access to super through super_moduleMethod
};
return my;
}(MODULE));
个人理解:
偷个懒不画内存图了,关于克隆前后,方法addPhoto( )改变产生的微小差异,通过口语化的注释来帮助理解,个人觉得很生动了。
var Module = Module || {};
Module = { //字面量表示法
publicStr : "i want to be a superman ...but ",
addPhoto : function (foo) {
console.log(foo+Module.publicStr);
}
};
Module2 = (function (old) {
var my = {},key;
//克隆
for (key in old) {
if (old.hasOwnProperty(key)) { //不检索原型链
my[key] = old[key];
}
}
return my;
}(Module));
Module2.addPhoto("i am isAre !! "); //继承了Module方法
console.log(Module2.publicStr);
Module.addPhoto = function(foo){
console.log("foo : "+foo);
}
Module.publicStr = "16 years old you fucked the sky ";
Module.addPhoto("i am isAre !!"); //Module方法修改注意是引用值发生改变,指向另一块内存堆区
//可以理解为搬家了,对于他来说已经从江北搬到了江南,家的外貌地址全然发生改变
Module2.addPhoto("i am isAre !!"); //Module2引用值仍是之前那个地址 所以console结果是:你16岁的那年对天放了一炮...
// 可以理解为旧友没有得知搬家消息,根据门牌号登门拜访,才发现家还是那个家,不曾改变过,但是里面住的人当然与现实有差距了
- webkit截图
Tips:如何使方法修改过后两个模块同步?
节约篇幅考虑,缩小代码量,直接修改上段代码11–15行为”my = old;”,console出现了两次结果:你16岁的那年对天放了一炮… |
跨文件访问私有对象
交叉访问匿名函数中的私有属性,默默增加新的函数方法,都被允许,这里面蕴藏着数据结构里面的锁机制。所以需要掌握的知识点:锁机制、异步的概念。
简单介绍一下锁机制吧:去做某些羞羞的事情,你来了,门锁上,你做事情,做完了,你出来;然后又来一位志同道合的人,他进去了,门锁上,他做事情,你又想做了,发现门是锁的,进不去。就这么简单的一回事~~反正我懂了…
- 代码结构:
var blogModule = (function (my) {
var _private = my._private = my._private || {},
_seal = my._seal = my._seal || function () {
delete my._private;
delete my._seal;
delete my._unseal;
},
_unseal = my._unseal = my._unseal || function () {
my._private = _private;
my._seal = _seal;
my._unseal = _unseal;
};
return my;
} (blogModule || {}));
- 个人理解:
var Module = (function (my) {
var _private = my._private = my._private || {}, //浅复制 实现交叉访问的大前提 通过删除 添加 my._private来进行模拟锁机制 进行数据访问操作等
_seal = my._seal = my._seal || function () {
delete my._private;
delete my._seal;
delete my._unseal;
},
_unseal = my._unseal = my._unseal || function () {
my._private = _private;
my._seal = _seal;
my._unseal = _unseal;
};
_seal(); //锁门不许进
_private.i = 10; //准备工作
if(_private.j){
_private.j = 1000;
}
my.add = function(my){ //搞事情
console.log(_private.i*_private.j);
}
_unseal(); //开门下一位
return my;
}(Module || {}));
var Module = (function (my) { //志同道合的路人甲到来
var _private = my._private = my._private || {},
_seal = my._seal = my._seal || function () {
delete my._private;
delete my._seal;
delete my._unseal;
},
_unseal = my._unseal = my._unseal || function () {
my._private = _private;
my._seal = _seal;
my._unseal = _unseal;
};
_seal(); //关门
_private.j = 100; //准备工作
if(_private.i){
_private.i = 1700;
}
my.divide = function(my){ //搞事情
console.log(_private.i/_private.j);
}
_unseal(); //下一位
return my;
}(Module || {}));
var Module = (function (my) { //不明事理的路人乙
var _private = my._private = my._private || {},
_seal = my._seal = my._seal || function () {
delete my._private;
delete my._seal;
delete my._unseal;
},
_unseal = my._unseal = my._unseal || function () {
my._private = _private;
my._seal = _seal;
my._unseal = _unseal;
};
_seal(); //关门
my.subtract = function(){ //不准备工作就搞事情
console.log(_private.j-_private.i);
}
_unseal(); //很快就出来了...
return my;
}(Module || {}));
Module.divide();
Module.add(); //add()就是乘法 大家发挥一下想象
Module.subtract();
- webkit截图
特别说明:假如,轻车熟路的路人乙第二次去干某事,这回准备了工作,那么最终输出结果,以路人乙为标准。请大家自行测试。
子模块
模块的儿子叫子模块,对模块的继承拓展有延伸作用,继承了爸爸的所有特色。有些时候,父亲老了,某些事情不能去做了,就生个儿子让儿子去做。只贴代码结构,大家有兴趣的可以自行对照着父模块的示例再敲一遍,加深印象~~~
- 代码结构:
blogModule.CommentSubModule = (function () {
var my = {};
// ...
return my;
} ());
CommentSubModule.subMethod();
结束语
耗时三天的研究或许是不值得的,有这个时间我可以堆好多页面出来,研究好多小的知识点,但是困难的东西研究出来,心里还是挺哈皮的~~
存在的知识盲区:umd模式。博文出现的小问题是浮点相加出现误差的问题,自行百度即可。
由于能力问题、时间问题或多或少存在着一些不足,请大牛不吝指教。
参考链接:
【原作者–我看不懂你们看着办】
【汤姆大叔的传送门】
【汤姆大叔翻译没他好的传送门】
【猪大大的传送门】
特别感谢:
segmentfault的shawncheung同学;zhihu的胡嵩。
【csdn博客管理员镇楼】