Javascript模块化编程

简介Javascript模块化编程的入门知识。

  • 最简单的实现
1
2
3
4
5
6
7
8
9
10
11
12
var Calc = function () {
var eqAdd = 10;

return {
add: function (x) {
return x + eqAdd;
}
};
};

var cal = new Calc();
console.log(cal.add(5)); // 15

问题是,每次都要new一个对象,占用内存。那我们干脆在全局定义一个实例,存储这个对象。

  • 先看下闭包
1
2
3
4
5
6
7
8
9
/* 方式1 */
(function () {
// 内部代码
}());

/* 方式2 */
(function () {
// 内部代码
})();
  • 引用一个全局变量
1
2
3
(function ($, Yahoo) {
// 这里的代码就可以访问全局对象jQuery和Yahoo了
} (jQuery, Yahoo));

这里只是访问了全局对象,我们经常需要声明全局变量。

  • 声明全局变量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var myModule = (function () {
var me = {}, name = "Tyrival";

function privateAddTopic (topic) {
return name + " " + topic;
}

me.addTopic = function (data) {
return privateAddTopic(data);
};

return me;
})

myModule.addTopic("has a new topic")
console.log(myModule.name); // Tyrival has a new topic

看起来这个已经可以了,但是这种做法适用于独立开发一个模块的情况,无法协作开发同一个myModule,浏览器同时引入多个定义myModule对象的代码时,后引入的会覆盖先引入的,所以,就需要使myModule可扩展。

  • 扩展
1
2
3
4
5
6
var myModule = (function (me) {
me.addPhoto = function () {
// do something
};
return me;
}(myModule))

这样就对myModule扩展了addPhoto方法,并且原来的addTopic方法还在。但是又出现新的问题,必须先写第4步的声明myModule,如果直接执行第5步,将myModule传为参数,将报错:

1
Uncaught TypeError: Cannot set property 'addPhoto' of undefined  // myModule is undefined
  • 松耦合扩展
1
2
3
4
5
6
7
8
9
10
/**
* 利用 var myModule = myModule || {}来实现Module模式的任意顺序加载
* 需要注意的是,此处必须加上var声明,否则其他文件读取不到这个myModule
*/
var myModule = (function (me) {
me.addPhoto = function () {
// do something
};
return me;
}(myModule || {}))

松耦合扩展也是有限制的,比如无法重写属性或函数,紧耦合扩展限制了加载顺序,同时提供了重载的功能。

  • 紧耦合模式
1
2
3
4
5
6
7
8
9
var myModule = (function (me) {
this.oldAddPhotoMethod = me.addPhoto;

me.addPhoto = function () {
// do something
};

return me;
}(myModule));

如果想使用重载前的方法,可以调用oldAddPhotoMethod方法。

  • 克隆与继承
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var myModule = (function (old) {
var me = {},
key;

for (key in old) {
if (old.hasOwnProperty (key) {
me[key] = old[key];
})
}

var oldAddPhotoMethod = old.addPhoto;
me.addPhoto = function () {
// do something
}

returm me;
}(myModule));

这种方式也会有个问题,就是新对象并没有复制老对象的属性和方法,而是引用。也就是说,如果老对象的属性或方法被修改后,新的对象也会同步变化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var blogModule = (function (me) {
var _private = me._private = me._private || {},

_seal = me._seal = me._seal || function () {
delete me._private;
delete me._seal;
delete me._unseal;
},

_unseal = me._unseal = me._unseal || function () {
me._private = _private;
me._seal = _seal;
me._unseal = _unseal;
};

return me;
}(blogModule || {}));

任何文件都可以对他们的局部变量_private设属性,并且设置对其他的文件也立即生效。一旦这个模块加载结束,应用会调用 blogModule._seal()”上锁”,这会阻止外部接入内部的_private。如果这个模块需要再次增生,应用的生命周期内,任何文件都可以调用_unseal() ”开锁”,然后再加载新文件。加载后再次调用_seal()”上锁”。

分享到