Skip to content
goto100 edited this page May 24, 2012 · 4 revisions

概览

  • 在objectjs中,代码的最基本组织形式是 模块 。除了浏览器原生成员,任何代码成员都应该属于某个模块;
  • objectjs中的模块只有在 被调用时 才会被执行,因此,它具有沙箱的特性,多次调用过程中,产生的模块实例不是同一个,避免了全局污染;

模块定义

object.define

object.define(id, dependencies?, factory);

id

当前模块的唯一标识,一般为一个路径;

dependencies

当前模块的依赖模块,用“,”(逗号)分隔,也可以传递一个数组,如['dom', 'events']

objectjs的Loader的创新点在于,在兼容CommonJS形式的模块的同时,支持传统的命名空间式 子模块 ,且两种模块可一起使用。两种形式是:

  • CommonJS形式,为一个路径形式,如a/b/c./b
  • ObjectJS形式,为命名空间形式,如a.b.c

两种依赖的区别是:

  • CommonJS依赖支持相对依赖,如./b则代表当前模块下名为b的模块;
  • ObjectJS依赖支持子模块,如依赖名为a.b.c的模块时,可通过获取到a的引用,调用a.b.c上的成员,同传统的 命名空间 式相同;
  • ObjectJS依赖的子模块在调用时会自动初始化其父模块;
  • 通过object.define指定了路径形式的id的模块,可直接通过ObjectJS的依赖形式获取其引用。

假设已定义3个模块,分别为aa/ba/b/c。 示例:

  • 定义a模块时,可通过./b依赖获取到a/b模块的引用;
  • 可通过a.b.c形式的依赖,将a/b模块的引用放到a模块的b成员上,将a/b/c模块的引用放到a/b模块的c成员上;
  • ObjectJS形式的模块依赖,并不支持相对引用,./b.c形式的依赖会找不到模块a/b.c

factory

模块构造函数,模块初始化时,会调用此函数并传递3个参数,分别为require、exports和module。

require

通过require获取依赖模块的引用。

object.define('mymodule', 'ui, ./submodule, ui/slider, ui.popup', function(require, exports) {
	var ui = require('ui'); // ui上有popup成员,没有slider成员
	var slider = require('ui/slider');
	var submodule = require('./submodule'); // 通过相对路径获取 mymodule/submodule 模块
});

require.async

用来异步加载模块。

object.define('mymodule', function(require, exports) {
	require.async('./b, ./c', function(b, c) {
	});
});

exports

在模块构造函数内部,通过exports提供此模块的成员(同this)。

在模块构造函数内部可通过this.__name__获取到当前模块的名称。

object.define('mymodule', function(require, exports, module) {
	// 模块函数
	exports.func = function() {};

	// 模块变量,this == exports
	this.foo = 1;

	// 模块类
	exports.MyClass = new Class(function() {});
	console.log(this.__name__);
});

同时,也可通过return返回模块的成员,此时,向exports上添加的任何成员都是无效的。

object.define('mymodule', function(require, exports, module) {
	// 通过return返回模块内容
	return {
		func: function() {},
		foo: 1,
		MyClass: new Class(function() {})
	};
});

object.add

objectjs还支持一种一种类似python的模块定义方式(object.add)。

  • object.define兼容SeaJS的模块书写规范,因此也可通过维护脚本兼容nodejs的模块;而add的模块从书写上并不完全兼容;

  • object.define支持通过require.async进行异步调用,而object.add不支持;

  • 通过add定义module的优势在于减少模块名依赖模块的代码量,自动获取依赖模块引用,减少代码量;

  • 根据以上区别,建议采用object.define声明模块;

  • 两者建立的模块可以互相依赖,并无兼容性问题。

    object.add(id, dependencies?, factory);

id和dependencies方法同object.define完全一致。

factory,模块构造函数,模块初始化时,会调用此函数并传递参数,第一个参数为exports,与回调函数中的this指向同一对象,可用于输出模块成员。

依赖的模块会通过函数参数 非重复顺序引用 的形式传递给主体函数。如果依赖模块是个子模块,则属于同一个父模块的多个子模块会放于同一父模块的引用中;

在模块构造函数中通过exportsthis输出模块成员,同object.define的规则是一样的。

object.add('ui', function(exports) {
	this.test = 1;
});

object.add('myobjectmodule', 'ui/slider, ui.popup', function(exports, slider, ui) {
	// 属于同一个父模块的多个子模块会放于同一父模块的引用中,子模块在调用时会自动初始化其父模块
	console.log(ui); // ==> {test: 1, popup: <Module popup>}
}

模块执行

object.execute

将一个已命名的模块直接执行,此时此模块主体函数中获取到的this.__name____main__而不是模块名

execute方法会产生一个运行时,一个运行时中的每个模块都会有一个且只有一个唯一实例,也就是说,在一个运行时中,一个模块如果被多个模块依赖调用,获取到的实例相同。

object.execute('mymodule'); // ==> __main__

利用此特性可在模块中判断当前__name__是否为__main__得知此模块是被依赖调用的还是被直接调用的。

object.use

形式同object.add类似,只是没有第一个模块名的参数

object.use就是object.add/define和object.execute的结合,object.use会向系统中注册一个匿名模块,并立刻execute。

object.use('mymodule, dom, event', function(mymodule, dom, event) {
	mymodule.func();
	console.log(mymodule.foo); // ==> 1
	console.log(this.__name__); // ==> __main__
}

动态加载

objectjs支持动态加载,减少网络请求数。需要在网页中预留需要动态加载的模块信息:

<script type="text/javascript" data-src="http://path/to/js.js" data-module="模块名称"></script>

这样,当object.use找不到需要的module时,会通过页面中的信息进行动态加载。

为了减少网络请求数,多个模块可以放置于同一个js中。data-module属性可以设置多个模块名:

<script type="text/javascript" data-src="http://path/to/3-modules-in-1.js" data-module="module1 module2 module3"></script>

高级特性

function模块

return返回模块的形式也可以返回一个function,就实现了一个function形式的模块。

object.add('jquery', function() { function jQuery() { // ... } jQuery.ajax = function() { } // ... return jQuery; });

object.use('jquery', function(exports, $) { // $是jquery模块的引用,jquery模块是一个function,意味着这个模块除了有子成员,还可以直接被调用 $.ajax() // 调用模块的某个成员 $('.editor') // 直接调用function模块 });

sys模块

系统中有一个内置的sys模块,其中的modules成员保存了此次use产生的运行时中的所有模块实例的引用

object.use('sys, mymodule', function(exports, sys, mymodule) {

// 在这里,sys.modules 保存着所有运行过程中用到的模块的引用
console.log(sys.modules); // ==> {dom: <Module dom>, event: <Module event>, sys: <Module sys>, mymodule: <Module mymodule>}

// 这样就能实现对某个运行时的模块进行修改、扩展的功能
var _submodule = sys['mumodule.submodule'];
if (_submodule) {
	_submodule.bar = 2;
}

});

object.execute用例

object.execute可在模块中判断当前__name__是否为__main__得知此模块是被依赖调用的还是被直接调用的, 因此可以判断直接调用时可自动执行一些初始化方法,实现模块的自初始化,将初始过程隐藏。

object.add('myeditor', function() {
	// 一个编辑器

	this.listen = function() {
		var eles = document.getElementsByClassName('editor');
		// ...
		// 初始化每个元素成为编辑器
	};

	// 自初始化
	if (this.__name__ == '__main__') {
		this.listen();
	}	
});

用object.use,需要知道myeditor的listen成员并经行调用,才可以在页面中初始化

object.use('myeditor', function(exports, myeditor) {
	// object.use生成myeditor实例时this.__name__为myeditor,不会进行自初始化
	myeditor.listen();
});

或用object.execute,自动初始化,无需得知其listen成员

object.execute('myeditor'); // 此时mymodule中的this.__name__为__main__

已注册模块

可通过 object._loader.lib 查看当前页面中已经注册的所有模块列表