JavaScript Module Pattern: In-Depth

本文翻译自大牛ben cherry的博客:http://www.adequatelygood.com/2010/3/JavaScript-Module-Pattern-In-Depth 。对自己的英文很有自信的同学可以直接去这个地址看原文。

翻译能力有限,欢迎指教~~

================  正文 =================

       Module pattern 是javascript的特有编码模式。(因为它没有java那样的访问指示符,如private、public等。module pattern就是来管理变量作用域的有效方式。)它的道理很简单,但是有一些高级特性却容易被忽视。下面,本文将对这些真正有用的特性进行详细讲述。

The Basics

      如果你对module pattern已经很了解,可以直接跳到"advanced patterns

Anonymous Closures(匿名闭包)

      这可能是js最有用的特性(没有之一)。你可以随时指定一个匿名函数,然后立即执行它。内部的变量将作为私有成员,在整个应用的生命周期内都有效。
(function () {
	// ... all vars and functions are in this scope only
	// still maintains access to all globals
}());
注意最后的()就是执行这个函数。这是语法要求,否则就仅是一个函数声明而已。

Global Import (导入全局变量)

      js有一个特性,即默认全局变量(implied globals):当你使用了一个变量名时,解释器会沿着作用域链向上寻找,直到找到这个名字,如果找不到就认为它是全局变量。这也就是说,如果在一个函数中你使用了一个未定义变量(而这可能只是由于你拼写错误导致的),那这个变量将引用全局变量的值,如果在全局空间中也不存在,那js将为你创建这个变量(在某些浏览器或strict模式下则可能报错)。这将污染你的全局变量空间,管理起来很不方便。
      幸运的是匿名闭包为我们提供了一个简单、高效的解决方法:
(function ($, YAHOO) {
	// now have access to globals jQuery (as $) and YAHOO in this code
}(jQuery, YAHOO));
通过把全局变量以参数的方式传入函数内部,就完成了全局变量的导入。

Module Export(模块导出)

      很多时候我们不仅要导入全局变量,也要把模块导出到全局空间供其他模块使用。通过在匿名函数中return一个obj,就可以轻松实现模块导出的功能。至此,基本的module pattern就讲完了。举例如下:
var MODULE = (function () {
	var my = {},
		privateVariable = 1;
	
	function privateMethod() {
		// ...
	}
	
	my.moduleProperty = 1;
	my.moduleMethod = function () {
		// ...
	};
	
	return my;
}());
这样,我们就在全局空间中声明了一个MODULE模块,该模块有1个公共属性:MODULE.moduleProperty和一个公共方法:MODULE.moduleMethod。同时还有一个privateVariable和privateMethod是私有的。

Advanced Patterns

      以刚才创建的MODULE为例,进一步讲述一些高级特性

Augmentation(扩展模块)

      上面讲述的模块其缺点是必须定义在一个文件中,从事过大型开发项目的人肯定能理解,如果能把模块分解到一些小文件将具有很大意义。以下将为你提供一个简单、完美的解决方案:
var MODULE = (function (my) {
	my.anotherMethod = function () {
		// added method...
	};

	return my;
}(MODULE));
可以容易的看出,我们的做法是:先将MODULE以参数形式传入匿名函数,为它增加方法,然后导出。这样MODULE就多了一个公共方法:MODULE.anotherMethod,同时之前的成员变量依然存在。尽管这里使用的是一个声明语句,但其实是没有必要的。因为在运行以上代码时要求MODULE必须已经定义。否则将报错。

Loose Augmentation(松散扩展)

      刚才也提到,上面扩展方法的不足是要求MODULE必须已经定义,这就要求不同的文件必须按照一定顺序加载,其实这是可以进一步优化的。js应用的一个巨大优势是各个脚本可以异步加载,松散扩展就充分利用了这个优势:
var MODULE = (function (my) {
	my.anotherMethod = function () {
		// added method...
	};

	return my;
}( MODULE || {} ));
在MODULE未定义时,直接使用空对象。这样脚本就能以任意顺序加载,就是这么简单!

Tight Augmentation(紧密扩展)

     松散扩展的麻烦是可能覆盖已经存在的方法,同时在初始化时无法使用其他脚本中定义的成员变量。紧密扩展解决了这个问题,但是失去了异步加载的优势。
var MODULE = (function (my) {
	var old_moduleMethod = my.moduleMethod;
	
	my.moduleMethod = function () {
		// method override, has access to old through old_moduleMethod...
	};
	
	return my;
}(MODULE));
和一般的augmentation相比它首先保持了旧有的成员,但是和松散扩展相比,它必须要求MODULE已经定义。

Cloning and 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));
这个方法的灵活性很差,比如hasOwnProperty无法检测原型链上的属性,同时该方法也不能复制内部的对象成员和函数。(很多类库和教材上都提供了更好的克隆方法,不知道作者为什么把这个放到Module pattern中讲)

cross-file private state(跨文件访问私有属性)

      一个脚本匿名函数中定义的内部变量是不能被其他脚本访问的。突破这层限制其实很简单,在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;
		};
	
	// permanent access to _private, _seal, and _unseal
	
	return my;
}(MODULE || {}));
其中_private是函数内部的状态变量,通常是无法被其他脚本访问的,但是通过MODULE的_private作为媒介就可以突破这个限制了。当然,Ben还提供了2个函数用来打开和关闭这个通道。当然,这个功能只有在load了以上脚本时才有效。

sub-modules(子模块)

这个是最简单的,要吃饭了。原文奉上

Our final advanced pattern is actually the simplest. There are many good cases for creating sub-modules. It is just like creating regular modules:

MODULE.sub = (function () {
	var my = {};
	// ...
	
	return my;
}());


While this may have been obvious, I thought it worth including. Sub-modules have all the advanced capabilities of normal modules, including augmentation and private state.

总结

以上所有特性大多都可以混合使用以生成更为复杂的应用程序。
关于性能,松散扩展能提高下载速度,但是加载可能会比较慢。在运行时,上面的特性对js性能不会有太大影响,甚至还可能因缩短了作用域链的查找,而有一定提升。
最后,附上一个例子。这个例子允许一个子模块动态的把自己加载到其父模块中,如果父模块不存在还会动态创建父模块。这种代码模式可以使一个大型项目并行加载,即使它存在复杂的层次结构。
var UTIL = (function (parent, $) {
	var my = parent.ajax = parent.ajax || {};
	
	my.get = function (url, params, callback) {
		// ok, so I'm cheating a bit :)
		return $.getJSON(url, params, callback);
	};
	
	// etc...
	
	return parent;
}(UTIL || {}, jQuery));


希望我的翻译对您有所帮助~~  吃饭咯
ps: 利用module pattern写的库函数:http://requirejs.org/docs/1.0/docs/api.html#define 可以直接使用哦







       

    


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值