Posted in Javascript onJuly 19, 2009
Ranges represent an interval of values. The value type just needs to be “compatible,” that is, to implement a succ method letting us step from one value to the next (its successor).
Prototype provides such a method for Number and String, but you are of course welcome to implement useful semantics in your own objects, in order to enable ranges based on them.
ObjectRange对象基本就是实现了连续的数字或者字符串,其中只包含一个方法,include,判断某个数字或者字符串是否在ObjectRange里。并且ObjectRange对象还混入了Enumerable的方法,所以可以直接在ObjectRange对象上调用Enumerable对象里面的方法。
//创建ObjectRange的便捷方法 function $R(start, end, exclusive) { return new ObjectRange(start, end, exclusive); } //创建ObjectRange对象并且继承自Enumerable var ObjectRange = Class.create(Enumerable, (function() { //初始化方法,exclusive为true时,不包含end数值,默认为undefined也就相当于false function initialize(start, end, exclusive) { this.start = start; this.end = end; this.exclusive = exclusive; } //覆盖Enumerable里面的_each方法,在遍历ObjectRange对象时需要用到此方法 function _each(iterator) { var value = this.start; while (this.include(value)) { iterator(value); value = value.succ(); } } //判断某个数值或者字符串是否包含在ObjectRange对象里 function include(value) { if (value < this.start) return false; if (this.exclusive) return value < this.end; return value <= this.end; } return { initialize: initialize, _each: _each, include: include }; })());
看一下示例,然后在详细解释一些细节:
$A($R('a', 'e')) // -> ['a', 'b', 'c', 'd', 'e'], no surprise there //千万不要尝试输出下面返回的结果,否者将会造成浏览器直接死掉 $A($R('ax', 'ba')) // -> Ouch! Humongous array, starting as ['ax', 'ay', 'az', 'a{', 'a|', 'a}', 'a~'...]
这里说一下$A($R('a', 'e')),如何返回值。首先看$A方法,前面的文章【Prototype 学习——工具函数学习($A方法)】中已经详细讲解了$A方法,不知道请自行参考。在$A方法里面有这样一句:if ('toArray' in Object(iterable)) return iterable.toArray();我们知道,ObjectRange里面混入了Enumerable里面的方法,也就是说间接实现了toArray方法,那么看一下Enumerable里面的toArray方法:
function toArray() { return this.map(); } //======> this.map() //我们注意到在返回的时候map方法被映射到了collect方法 return { //... collect: collect, map: collect, //... } //======> collect() //在本例中这个方法其实就相当于返回一个数组,因为传进来的参数都是undefined。这里面有一个this.each方法,继续查看 function collect(iterator, context) { iterator = iterator || Prototype.K; var results = []; this.each(function(value, index) { results.push(iterator.call(context, value, index)); }); return results; } //======> this.each() //终于看到this._each了,现在明白为什么ObjectRange里面会重写了_each方法了吧。在遍历的时候要用到这个方法 function each(iterator, context) { var index = 0; try { this._each(function(value) { iterator.call(context, value, index++); }); } catch (e) { if (e != $break) throw e; } return this; } //======> this._each() //详细说明一下this._each() //关键就是succ()这个方法,因为_each里面使用这个方法产生下一个数值。 //这个succ()在哪里呢?在Number.prototype和String.prototype里面定义了这个方法 function _each(iterator) { var value = this.start; while (this.include(value)) { iterator(value); value = value.succ(); } } //下面两个方法我就不讲了吧。 //======> String.prototype.succ() function succ() { return this.slice(0, this.length - 1) + String.fromCharCode(this.charCodeAt(this.length - 1) + 1); } //======> Number.prototype.succ() function succ() { return this + 1; } //综上所述,如果你自己想定义其它类型的ObjectRange对象,譬如Date类型,那么你就要自己实现succ()方法,用来生成连续的对象
上面的流程将清楚了,但一些函数没有仔细讲,等讲到这些对象的时候在仔细说明里面的函数。下面看几个include的示例吧:
$R(1, 10).include(5) // -> true $R('a', 'h').include('x') // -> false $R(1, 10).include(10) // -> true $R(1, 10, true).include(10) // -> false
Prototype ObjectRange对象学习
声明:登载此文出于传递更多信息之目的,并不意味着赞同其观点或证实其描述。
Reply on: @reply_date@
@reply_contents@