[toc]
## jQuery 的 prevObject 對象
當我們新建一個 jQuery 對象的時候,都會在其屬性中發現一個 prevObject 的屬性,如果單單從名字來看的,“前一個對象”,那么它到底是怎么用的呢。

在 jQuery 對象的內部,有著一個 jQuery 對象棧,用來維護所有已經操作過的 jQuery 對象。這樣子的話可以寫出很流暢的 jQuery 代碼,比如操作父元素,操作子元素,再回到父元素的時候就很方便。
但實際上,這個棧是不存在的,我們知道數組可以當作棧或隊列來使用,是不是說 jQuery 內部有這么個數組來存放這個對象棧呢。答案是 no,為什么,因為沒必要這么麻煩。這樣來想一想,如果每一個 jQuery 對象都有一個指針指向上一個對象的話,也就間接組成了一個 jQuery 對象棧。如果棧只有一個元素,prevObject 就默認指向 document。prevObject 是干什么用的,就是來實現對象棧的。比如:
```
<div id="view">
<h1 class="header">標題</h1>
<p>container</p>
<strong class="espe">重點</strong>
</div>
```
對于上面的 html:
```
var $view = $("#view");
// $header 是由 $view 操作得到的
var $header = $view.find('.header');
$header.prevObject === $view; // true
```
不過在使用的時候,都是忽略對象棧而定義不同的 jQuery 對象來指向父元素和子元素,就像上面的例子那樣,既然定義了 $view 和 $header,就不需要 prevObject 了。
## jQuery.fn.end 和 jQuery.fn.addBack
這兩個函數其實就是 prevObject 的應用,舉個例子就能弄明白了,仍然是上面的那個 html:
```
$('#view').find('.header').css({'color': 'red'})
.end()
.find('.espe').css({'color': 'white'})
```
加了 end之后,當前執行的 jQuery 對象就變成 $('#view')了,所以可以繼續執行 find 操作等等,如果不加的話,這段話是不能執行成功的。可見,如果這樣子寫 jQuery 不僅寫法優雅,而且還很高效。
end 和 addBack 是有區別的,end() 函數只是單純的進行出棧操作,并返回出棧的這個 jQuery 對象,而 addBack() 函數不會執行出棧,而是把棧頂對象和當前的對象組成一個新對象,入棧:
```
$('#view').find('.header').nextAll()
.addBack()
.css({'color': 'red'}) // 顏色全紅
```
上面的代碼,會使得 #view 的三個子元素的顏色都設置為紅色。
## 相關函數源碼
重新來看下 pushStack 函數吧,這個函數不僅在 fn.find() 函數中出現,好多涉及 jQuery dom 操作的原型函數都出現了,而且之前介紹的時候,忽略了一個重要的部分 prevObject 對象:
```
jQuery.fn.pushStack = function (elems) {
// 將 elems 合并到 jQuery 對象中
var ret = jQuery.merge(this.constructor(), elems);
// 實現對象棧
ret.prevObject = this;
// 返回
return ret;
}
```
pushStack 生成了一個新 jQuery 對象 ret,ret 的 prevObject 屬性是指向調用 pushStack 函數的那個 jQuery 對象的,這樣就形成了一個棧鏈,其它原型方法如 find、nextAll、filter 等都可以調用 pushStack 函數,返回一個新的 jQuery 對象并維持對象棧。
我們知道了所有 jQuery 方法都有一個 prevObject 屬性的情況下,來看看 end 方法:
```
jQuery.fn.end = function () {
return this.prevObject || this.constructor();
}
```
還有 addBack 方法:
```
jQuery.fn.addBack = function (selector) {
// 可以看出有參數的 addBack 會對 prevObject 進行過濾
return this.add(selector == null ? this.prevObject : this.prevObject.filter(selector));
}
```
里面穿插了一個 fn.add 方法:
```
jQuery.fn.add = function (selector, context) {
return this.pushStack(
jQuery.uniqueSort(
jQuery.merge(this.get(), jQuery(selector, context))));
}
```
應該不需要解釋吧,源碼一清二楚。
## 總結
我之前就已經說過,很少會使用 end 或 pushStack 方法,而在 jQuery 內部的原型方法中,比如 find、nextAll、filter 等,被頻繁使用,這種封裝一個 jQuery 的方法很棒,會把對象棧給維護得很好。無論如何,這就是 jQuery 的魅力吧!