读Zepto源码之集合操作
接下来几个篇章,都会解读 zepto 中的跟 dom
相关的方法,也即源码 $.fn
对象中的方法。
读Zepto源码系列文章已经放到了github上,欢迎star: reading-zepto
源码版本
本文阅读的源码为 zepto1.2.0
.forEach()
forEach: emptyArray.forEach
因为 zepto 的 dom
集合是类数组,所以这里只是简单地复制了数组的 forEach
方法。
具体的 forEach
的用法见文档:Array.prototype.forEach()
.reduce()
reduce: emptyArray.reduce
简单地复制了数组的 reduce
方法。
具体的 reduce
的用法见文档:Array.prototype.reduce()
.push()
push: emptyArray.push
简单地复制了数组的 push
方法。
具体的 push
的用法见文档:Array.prototype.push()
.sort()
sort: emptyArray.sort
简单地复制了数组的 sort
方法。
具体的 sort
的用法见文档:Array.prototype.sort()
.splice()
splice: emptyArray.splice
简单地复制了数组的 splice
方法。
具体的 splice
的用法见文档:Array.prototype.splice()
.indexOf()
indexOf: emptyArray.indexOf
简单地复制了数组的 indexOf
方法。
具体的 indexOf
的用法见文档:Array.prototype.indexOf()
.get()
get: function(idx) {
return idx === undefined ? slice.call(this) : this[idx >= 0 ? idx : idx + this.length]
},
这个方法用来获取指定索引值的元素。
不传参(idx === undefined
)时,不传参调用数组的 slice
方法,将集合中的所有元素返回。
当传递的参数大于或等于零(idx
)时,返回相应索引值的元素 this[idx]
,如果为负数,则倒数返回this.[idx + this.length]
。
例如 $('li').get(-1)
返回的是倒数第1个元素,也即最后一个元素
.toArray()
toArray: function() { return this.get() }
toArray
方法是将元素的类数组变成纯数组。toArray
内部不传参调用 get
方法,上面已经分析了,当不传参数时,get
方法调用的是数组方法 slice
, 返回的自然就是纯数组了。
.size()
size: function() {
return this.length
}
size
方法返回的是集合中的 length
属性,也即集合中元素的个数。
.concat()
concat: function() {
var i, value, args = []
for (i = 0; i < arguments.length; i++) {
value = arguments[i]
args[i] = zepto.isZ(value) ? value.toArray() : value
}
return concat.apply(zepto.isZ(this) ? this.toArray() : this, args)
},
数组中也有对应的 concat
方法,为什么不能像上面的方法那样直接调用呢?
这是因为 $.fn
其实是一个类数组对象,并不是真正的数组,如果直接调用 concat
会直接把整个 $.fn
当成数组的一个 item
合并到数组中。
for (i = 0; i < arguments.length; i++) {
value = arguments[i]
args[i] = zepto.isZ(value) ? value.toArray() : value
}
这段是对每个参数进行判断,如果参数是 zepto
的集合(zepto.isZ(value)
),就先调用 toArray
方法,转换成纯数组。
return concat.apply(zepto.isZ(this) ? this.toArray() : this, args)
这段同样对 this
进行了判断,如果为 zepto
集合,也先转换成数组。所以调用 concat
后返回的是纯数组,不再是 zepto
集合。
.map()
map: function(fn) {
return $($.map(this, function(el, i) { return fn.call(el, i, el) }))
}
map
方法的内部调用的是 zepto
的工具函数 $.map
,这在之前已经在《读Zepto源码之工具函数》做过了分析。
return fn.call(el, i, el)
map
方法对回调也做了包装,call
的第一个参数为 el
,因此可以在 map
的回调中通过 this
来拿到每个元素。
map
方法对 $.map
返回的数组调用了 $()
方法,将返回的数组再次包装成 zepto
对象,因此调用 map
方法后得到的数组,同样具有 zepto
集合中的方法。
.slice()
slice: function() {
return $(slice.apply(this, arguments))
}
slice
同样没有直接用数组的原生方法,也像 map
方法一样,将返回的数组再次包装成 zepto
对象。
.each()
each: function(callback) {
emptyArray.every.call(this, function(el, idx) {
return callback.call(el, idx, el) !== false
})
return this
},
zepto
的 each
方法比较巧妙,在方法内部,调用的其实是数组的 every
方法,every
遇到 false
时就会中止遍历,zepto
也正是利用 every
这种特性,让 each
方法也具有了中止遍历的能力,当 callback
返回的值为布尔值 false
时,中止遍历,注意这里用了 !==
,因为 callback
如果没有返回值时,得到的值会是 undefined
,这种情况是需要排除的。
同样,each
的回调中也是可以用 this
拿到每个元素的。
注意,each
方法最后返回的是 this
, 所以在 each
调用完后,还可以继续调用 集合中的其他方法,这就是 zepto
的链式调用,这个跟 map
方法中返回 zepto
集合的原理差不多,只不过 each
返回的是跟原来一样的集合,map
方法返回的是映射后的集合。
.add()
add: function(selector, context) {
return $(uniq(this.concat($(selector, context))))
}
add
可以传递两个参数,selector
和 context
,即选择器和上下文。
add
调用 $(selector, context)
来获取符合条件的集合元素,这在上篇文章《读Zepto源码之神奇的$》已经有详细的论述。
然后调用 concat
方法来合并两个集合,用内部方法 uniq
来过滤掉重复的项,uniq
方法在《读Zepto源码之内部方法》已经有论述。最后也是返回一个 zepto
集合。
系列文章
参考
- Array.prototype.forEach()
- Array.prototype.reduce()
- Array.prototype.push()
- Array.prototype.sort()
- Array.prototype.splice()
- Array.prototype.indexOf()
License
作者:对角另一面