在上面的一片文章《三、(补充)jQuery源码中init方法详解》中,我们介绍了jQuery实例方法init的实现逻辑,本篇文章,我们再来介绍一个jQuery中比较基础,也比较常用的方法pushStack,这个方法实现了jQuery中出栈、入栈的相关操作。
首先,来看一下它的源码
pushStack: function( elems ) {
// Build a new jQuery matched element set
var ret = jQuery.merge( this.constructor(), elems );
// Add the old object onto the stack (as a reference)
ret.prevObject = this;
ret.context = this.context;
//Return the newly-formed element set
return ret;
}
一、pushStack方法逻辑分析
1、pushStack方法的使用
<div>div</div>
<span>span</span>
<script>
$('div').pushStack($('span')).css('background','red')
</script>
这段代码的运行结果如下

为什么会出现这样的结果呢?我们执行下面的代码。
<div>div</div>
<span>span</span>
<script>
console.log($('div').pushStack($('span')))
</script>

可以看到返回的结果确实是jQuery('span')对象,不过这个对象中,多出来了一个我们比较默认的属性prevObject,并且这个属性指向的是jQuery('div')这个元素,哈哈 ,这一切的一切到底是什么意思呀!不要着急,请您看下面的解释。
2、pushStack方法的逻辑分析
在执行完代码$('div').pushStack($('span'))
后,jquery会为我们维护一个调用栈,栈的结构如下图所示:

从图中,我们可以看出,将
$('div')
压入到了栈底,$('span')
放到了$('div')
的上一层,$('span')
中含有一个属性指向$('div')
的属性prevObject,所以上面的代码最后返回的结果就是栈顶元素$('span')
,所以会得出上面的那种结果。结合上面的分析,我们再来看一下源码.
pushStack: function( elems ) {
// Build a new jQuery matched element set
var ret = jQuery.merge( this.constructor(), elems );
// Add the old object onto the stack (as a reference)
ret.prevObject = this;
ret.context = this.context;
//Return the newly-formed element set
return ret;
源码中通过jQuery,merge方法合并了新的jquery对象ret
,在ret
上面添加prevObject
属性,并指向调用pushStack方法的jquery对象(上层对象),同时将上层对象的上下文对象context
传递给ret
,最后将ret
对象返回。通过上面的逻辑,我们就可以维护一个jquery对象的调用栈了。
这个方法在平常的开发中用的比较少,这是因为pushStack
这个方法是提供给jquery内部使用的,在外部并没有多少开发场景可以真正地使用到它。
二、pushStack在其他方法中使用场景
我们在上面的分析中提及到了prevObject这个属性,它指向的是调用栈中的下一层jquery对象,那下面,我们就来仔细看一下下面这个属性使用场景吧。
1、在jQuery实例方法end中的使用
<div>div</div>
<span>span</span>
<script>
$('div').pushStack($('span')).css('background','red').end().css('background','blue')
</script>
上面的这段代码的运行结果如下:

对于上面的结果,我们通过分析源码找到答案。看到了吗?是返回了
prevObject
属性,自然会指向$('div')
这个jQuery对象。
end: function() {
return this.prevObject || this.constructor(null);
}
2、在jQuery实例方法slice中的使用
<div>div</div>
<div>div</div>
<div>div</div>
<div>div</div>
<span>span</span>
<script>
$('div').slice(1,3).css('background','red')
</script>
上面代码的运行结果如下:

我们依旧还是通过源码来分析一下这段代码的执行逻辑。哈哈,很简单吧,通过
apply
方法调用数组的slice
方法,然后再将其推入到栈顶来完成我们的逻辑。
slice: function() {
return this.pushStack( core_slice.apply( this, arguments ) );
}
3、在jQuery实例方法eq中的使用
<div>div</div>
<div>div</div>
<div>div</div>
<div>div</div>
<span>span</span>
<script>
$('div').eq(1).css('background','red')
</script>
上面的代码运行结果如下:

这段代码我们都非常熟悉,同样,让我们来看一下它的实现原理吧。
eq: function( i ) {
var len = this.length,
j = +i + ( i < 0 ? len : 0 );
return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] );
}
其实jquery中eq
这个方法中还是有很多设计令人惊叹的。首先j = +i + ( i < 0 ? len : 0 )
,大家知道为什么是+i
,而不直接是i
,这是为了防止某些人传递给eq
方法的是 字符串‘-1’
,而不是number类型的-1
,此处通过+
将字符串转化成数字。然后就是调用pushStack
方法,将其放到栈的最顶端。
当然,在jquery中用到pushStack的地方还有很多,此处并没有意义罗列。最后感谢您的阅读!
网友评论