var SmartQueue = window.SmartQueue || {}; SmartQueue.version = '0.1';初始化的时候,如果碰到命名空间冲突就把它拉过来用。通常这个冲突是由重复引用组件代码导致的,因此“拉过来用”会将对象以同样的实现重写一次;最坏的情况下,如果碰巧页面上另一个对象也叫 SmartQueue, 那不好意思了,我会覆盖你的实现——如果没有进一步的命名冲突,基本上两个组件可以相安无事地运行。同时顺便给它一个版本号。 接着,按三个优先级为 SmartQueue 创建三个队列:
var Q = SmartQueue.Queue = [[], [], []];
每个都是空数组,因为还没有任务加进去嘛。又顺便给它建个“快捷方式”,后面要访问数组直接写 Q[n] 就可以啦。
接下来,我们的主角 Task 隆重登场——怎么 new 一个 Task, 定义在这里:
var T = SmartQueue.Task = function(fn, level, name, dependencies) { if(typeof fn !== FUNCTION) { throw new Error('Invalid argument type: fn.'); } this.fn = fn; this.level = _validateLevel(level) ? level : LEVEL_NORMAL; // detect type of name this.name = typeof name === STRING && name ? name : 't' + _id++; // dependencies could be retrieved as an 'Object', so use instanceof instead. this.dependencies = dependencies instanceof Array ? dependencies : []; };里面的具体细节就不说了,有必要的注释,一般我们的代码也能做到自我描述,后面代码也是这样。这里告诉客户(使用者):你想新建一个 SmartQueue.Task 实例,就要至少传一个参数给这个构造函数(后 3 个都可以省略进行缺省处理),否则抛出异常伺候。 但是这还不够,有时候,客户希望从已有 Task 克隆一个新实例,或是从一个“残废体”(具有部分 Task 属性的对象)修复出“健康体”(真正的 Task 对象实例),通过上面的构造方式就有点不爽了——客户得这样写:
var task1 = new SmartQueue.Task(obj.fn, 1, '', obj.dependencies);我很懒,我只想传 fn 和 dependencies 两个属性,不想做额外的事情。好吧,我们来重构一下构造函数:
var _setupTask = function(fn, level, name, dependencies) { if(typeof fn !== FUNCTION) { throw new Error('Invalid argument type: fn.'); } this.fn = fn; this.level = _validateLevel(level) ? level : LEVEL_NORMAL; // detect type of name this.name = typeof name === STRING && name ? name : 't' + _id++; // dependencies could be retrieved as an 'Object', so use instanceof instead. this.dependencies = dependencies instanceof Array ? dependencies : []; }; var T = SmartQueue.Task = function(task) { if(arguments.length > 1) { _setupTask.apply(this, arguments); } else { _setupTask.call(this, task.fn, task.level, task.name, task.dependencies); } // init context/scope and data for the task. this.context = task.context || window; this.data = task.data || {}; };如此一来,原来的构造方式可以继续工作,而上面的懒人可以这样传入一个“残废体”:
var task1 = new SmartQueue.Task({fn: obj.fn, dependencies: obj.dependencies});当构造函数收到多个参数时,按之前的方案等同处理;否则,视唯一的参数为 Task 对象或“残废体”。这里通过 JavaScript 中的 [code]apply[/code]/[code]call[/code] 方法将新实例传给重构出来的 [code]_setupTask[/code] 方法,作为该方法的上下文 (context, 也有称为 scope), [code]apply[/code]/[code]call[/code] 是 JavaScript 在方法之间传递上下文的法宝,要用心体会哦。同时,允许用户定义 [code]task.fn[/code] 在执行时的上下文,并将自定义的数据传递给执行中的 fn. 经典的 JavaScript 对象三段式是什么? [list=1] [*]定义对象的构造函数 [/*][*]在原型上定义属性和方法 [/*][*]new 对象,拿来用 [/*][/list] 所以,下面要为 [code]SmartQueue.Task[/code] 对象的原型定义属性和方法。上期分析过 Task (任务)有几个属性和方法,部分属性我们已经在 [code]_setupTask[/code] 中定义了,下面是原型提供的属性和方法:
T.prototype = {
enabled: true,
register: function() {
var queue = Q[this.level];
if(_findTask(queue, this.name) !== -1) {
throw new Error('Specified name exists: ' + this.name);
}
queue.push(this);
},
changeTo: function(level) {
if(!_validateLevel(level)) {
throw new Error('Invalid argument: level');
}
level = parseInt(level, 10);
if(this.level === level) {
return;
}
Q[this.level].remove(this);
this.level = level;
this.register();
},
execute: function() {
if(this.enabled) {
// pass context and data
this.fn.call(this.context, this.data);
}
},
toString: function() {
var str = this.name;
if(this.dependencies.length) {
str += ' depends on: [' + this.dependencies.join(', ') + ']';
}
return str;
}
};
如你所见,逻辑非常简单,也许你已经在一分钟内扫过了代码,嘴角不经意间露出一丝心领神会。不过,这里要说的是简单而且通常最不被重视的 [code]toString[/code] 方法。在一些高级语言中,为自定义对象实现 [code]toString[/code] 方法被作为最佳实践准则而推荐,为什么呢?因为 [code]toString[/code] 可以很方便地在调试器中提供有用的信息,可以方便地将对象基本信息写入日志;在统一的编程模式中,实现 [code]toString[/code] 可以让你少写一些代码。
嗯,我们继续推进,我们要实现 SmartQueue 的具体功能。上期分析过,SmartQueue 只有一个实例,因此我们决定直接在 SmartQueue 下面创建方法:
SmartQueue.init = function() { Q.forEach(function(queue) { queue.length = 0; }); };这里用到 JavaScript 1.6 为 Array 对象提供的遍历方法 [code]forEach[/code]. 之所以这样写是因为我们假定“外部代码”已经在前面运行过了。设置 Array 对象的 [code]length[/code] 属性为 [code]0[/code] 导致,它被清空并且释放所有的项(数组单元)。 最后一个方法 [code]fire[/code], 是整个组件最主要的方法,它负责对所有任务队列进行排序,并逐个执行。由于代码稍长了一点,这里只介绍排序使用的算法和实现方式,完整代码在[url=http://code.google.com/p/janlay/source/browse/trunk/smart-queue/src/smart-queue.js]这里[/url]。
var _dirty = true, // A flag indicates weather the Queue need to be fired. _sorted = [], index; // Sort all Queues. // ref: [url=http://en.wikipedia.org/wiki/Topological_sorting]http://en.wikipedia.org/wiki/Topological_sorting[/url] var _visit = function(queue, task) { if(task._visited >= 1) { task._visited++; return; } task._visited = 1; // find out and visit all dependencies. var dependencies = [], i; task.dependencies.forEach(function(dependency) { i = _findTask(queue, dependency); if(i != -1) { dependencies.push(queue[i]); } }); dependencies.forEach(function(t) { _visit(queue, t); }); if(task._visited === 1) { _sorted[index].push(task); } }, _start = function(queue) { queue.forEach(function(task) { _visit(queue, task); }); }, _sort = function(suppress) { for(index = LEVEL_LOW; index <= LEVEL_HIGH; index++) { var queue = Q[index]; _sorted[index] = []; _start(queue); if(!suppress && queue.length > _sorted[index].length) { throw new Error('Cycle found in queue: ' + queue); } } };我们将按任务指定的依赖关系对同一优先级内的任务进行排序,确保被依赖的任务在设置依赖的任务之前运行。这是一个典型的深度优先的[url=http://baike.baidu.com/view/288212.html]拓扑排序[/url]问题,[url=http://en.wikipedia.org/wiki/Topological_sorting]维基百科[/url]提供了一个深度优先排序算法,大致描述如下:
var t1 = new SmartQueue.Task(function() { alert("Hello, world!"); }), t2 = new SmartQueue.Task(function() { alert("High level task has name"); }, 2, 'myname'); t1.register(); t2.register(); SmartQueue.fire();更多功能,如任务的依赖,等待你去发掘哦。 本期贴出的代码都是一些局部片段,部分 helper 方法代码没有贴出来。查看完整的代码请访问[url=http://code.google.com/p/janlay/source/browse/trunk/smart-queue/src/smart-queue.js]这里[/url]。后面我们将介绍如何管理组件文件,以及构建组件,下期不见不散哦。
机械节能产品生产企业官网模板...
大气智能家居家具装修装饰类企业通用网站模板...
礼品公司网站模板
宽屏简约大气婚纱摄影影楼模板...
蓝白WAP手机综合医院类整站源码(独立后台)...苏ICP备2024110244号-2 苏公网安备32050702011978号 增值电信业务经营许可证编号:苏B2-20251499 | Copyright 2018 - 2025 源码网商城 (www.ymwmall.com) 版权所有