循环意味着我们的思维处于一种反复回绕的状态, 而线性化之后则沿着一个方向直线前进, 极大减轻了思维负担, 提高了代码的可组合性. 为了减少调用链的中断, jQuery发明了一个绝妙的主意: jQuery包装对象本身类似数组(集合). 集合可以映射到新的集合, 集合可以限制到自己的子集合,调用的发起者是集合,返回结果也是集合,集合可以发生结构上的某种变化但它还是集合, 集合是某种概念上的不动点,这是从函数式语言中吸取的设计思想。集合操作是太常见的操作, 在java中我们很容易发现大量所谓的封装函数其实就是在封装一些集合遍历操作, 而在jQuery中集合操作因为太直白而不需要封装.
链式调用意味着我们始终拥有一个“当前”对象,所有的操作都是针对这一当前对象进行。这对应于如下公式
x += dx
调用链的每一步都是对当前对象的增量描述,是针对最终目标的逐步细化过程。Witrix平台中对这一思想也有着广泛的应用。特别是为了实现平台机制与业务代码的融合,平台会提供对象(容器)的缺省内容,而业务代码可以在此基础上进行逐步细化的修正,包括取消缺省的设置等。
话说回来, 虽然表面上jQuery的链式调用很简单, 内部实现的时候却必须自己多写一层循环, 因为编译器并不知道"自动应用于集合中每个元素"这回事.
复制代码 代码如下:
$.fn['someFunc'] = function(){
return this.each(function(){
jQuery.someFunc(this,...);
}
}
6. data: 统一数据管理
作为一个js库,它必须解决的一个大问题就是js对象与DOM节点之间的状态关联与协同管理问题。有些js库选择以js对象为主,在js对象的成员变量中保存DOM节点指针,访问时总是以js对象为入口点,通过js函数间接操作DOM对象。在这种封装下,DOM节点其实只是作为界面展现的一种底层“汇编”而已。jQuery的选择与Witrix平台类似,都是以HTML自身结构为基础,通过js增强(enhance)DOM节点的功能,将它提升为一个具有复杂行为的扩展对象。这里的思想是非侵入式设计(non-intrusive)和优雅退化机制(graceful degradation)。语义结构在基础的HTML层面是完整的,js的作用是增强了交互行为,控制了展现形式。
如果每次我们都通过$('#my')的方式来访问相应的包装对象,那么一些需要长期保持的状态变量保存在什么地方呢?jQuery提供了一个统一的全局数据管理机制。
获取数据:
复制代码 代码如下:
$('#my').data('myAttr')
设置数据:
复制代码 代码如下:
$('#my').data('myAttr',3);
这一机制自然融合了对HTML5的data属性的处理
复制代码 代码如下:
<input data-my-attr="4" ... />
通过 $('#my').data('myAttr')将可以读取到HTML中设置的数据。
第一次访问data时,jQuery将为DOM节点分配一个唯一的uuid, 然后设置在DOM节点的一个特定的expando属性上, jQuery保证这个uuid在本页面中不重复。
复制代码 代码如下:
elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];
以上代码可以同时处理DOM节点和纯js对象的情况。如果是js对象,则data直接放置在js对象自身中,而如果是DOM节点,则通过cache统一管理。
因为所有的数据都是通过data机制统一管理的,特别是包括所有事件监听函数(data.events),因此jQuery可以安全的实现资源管理。在clone节点的时候,可以自动clone其相关的事件监听函数。而当DOM节点的内容被替换或者DOM节点被销毁的时候,jQuery也可以自动解除事件监听函数, 并安全的释放相关的js数据。
7. event:统一事件模型
"事件沿着对象树传播"这一图景是面向对象界面编程模型的精髓所在。对象的复合构成对界面结构的一个稳定的描述,事件不断在对象树的某个节点发生,并通过冒泡机制向上传播。对象树很自然的成为一个控制结构,我们可以在父节点上监听所有子节点上的事件,而不用明确与每一个子节点建立关联。