2011年3月14日 星期一

jQuery .end()

在 jQuery 的語法中,有提供一個 .end() 的方法,最近才開始有使用到,覺得其重要性其實蠻高的,所以稍微紀錄一下自己的心得。

jQuery 的陳述句,具有串鏈(chaining)的特性,譬如說:

$("p").find("span").find(".read")

這語法,是先找出目前頁面上所有 <p> 的標籤,假設有10個,
接著在從目前的篩選結果中找出有<span>的標籤,可能只有5個,
最後,在從這剩下的結果中找出 class=read的項目,可能只剩下2個了。
透過串鏈的好處,可以讓我們一層一層去篩選我們想要的結果,但有沒有想過,這一串 jQuery 語法,雖然可以幫我找到想要的結果,但如果我想同時去處理兩種以上的結果時,該怎麼辦呢?從上面的例子中,我最後是要找從<span>中找出 class name 等於 read 的項目,但如果我也想同時在一個 jQuery 中處理 class name 等於 write 的項目時,可行嗎?

在 jQuery 裡,為了解決上述的疑問,他提供了 .end() 的語法,其意義就好比告訴人家,從這裡開始,回到上一層篩選結果。我們每執行一次具有破壞篩選結果的語法時,就會影響到存放目前篩選結果的堆疊。譬如上述例子,執行了 $("p"),存放在結果堆疊裡一共有10個值,但當你接著執行 .find("span") 之後,堆疊結果加進了第二次的5個查詢結果。最後一次執行了 .find("read"),則堆疊中加入了2個查詢結果。

當你執行了 .end(),則會從結果堆疊中 pop 最上層的結果,也就是把第三次的篩選結果移除,而接著要處理的項目就是 $("p").find("span") 的篩選結果了。所以,如果你想要進一步找出 class name 等於 write 的項目時,則只要在 .end()之後,加上 .find(".write")。
另外一個值得注意的是,由上圖可知,你執行一次 .end() 就會將結果堆疊 pop 一次,那是否可以執行 2 次呢?也就是,可以執行 .end().end() 嗎?可以的,由上圖可知,若您連續兩次的 .end().end() ,就會連續 pop 兩次,則結果堆疊就會只剩下  $("p") 。那如果連續 .end().end().end() 三次呢?看來你的劣根性跟我一樣,如果這樣玩的話,jQuery 就會....回傳空值了,而你的結果堆疊也就被清空了。

上面所提及的「具有破壞篩選結果的語法」,在 jQuery 說明文件中,列舉了以下語法都是具有破壞篩選結果的語法:
 add(),andSelf(),children(),closes(),filter(),find(),map(),next(),nextAll(),not(),parent(),parents(),prev(),prevAll(),siblings(),slice(),clone(),appendTo(),prependTo(),insertBefore(),insertAfter(),replaceAll()

可能會覺得奇怪,為何一定要這麼麻煩,將兩個查詢結果全部寫在一句 jQuery,分兩次寫不可以嗎?原先上面的查詢語法是:

$("p").find("span").find(".read").end().find("write")

難道不能寫成:

$("p").find("span").find(".read")
$("p").find("span").find(".write")

這樣嗎?

這種寫法,我完全同意,但當我讀了 finalevil 的一篇 [jQuery]jQuery Chaining(鏈式語法) 文章後,深深覺得 .end() 的價值排名提昇了不少。因為瀏覽器會將網頁分析後產生 Dom Tree,而 jQuery 的查詢就是針對這個 Dom Tree 去搜尋。只要是 Tree 的搜尋,都是會花時間的。如果一次可以尋訪完,你分了好幾次,當你的 jQuery 查詢次數變多時,或是 Dom Tree 是個非常大的一棵樹,想必您對效能的體會應該會更高些。以上心得,與有緣人共勉之~

參考連結:
01.A Complete Guide To jQuery – Learning The Basics
02.[jQuery]jQuery Chaining(鏈式語法)

5 則留言:

  1. 不用客氣...
    很開心對你有幫助。

    回覆刪除
  2. 您好!關於end方法
    我個人偏好使用"暫存物件"方式來解決
    鏈式程式碼長了就顯得"不易閱讀"

    var $paragraph = $("p"); // 先暫存jQuery變數,以便之後不用重複尋訪DOMTree及重複製造jQuery object

    $paragraph.find(".read");
    $paragraph.find(".write");

    不知道這樣的方式跟end方法哪個比較合適呢

    回覆刪除
  3. 用 .end()串鏈方式看似很不容易閱讀,但卻不需額外記憶體空間來配合。您提到的暫存物件方法,的確讓設計師很容易清楚程式邏輯,但卻需要一塊記憶體去暫存。

    個人是覺得程式的可讀性與執行效率,通常不容易兩者兼具,而這兩者沒有絕對的對錯關係,只是適用性的問題。但我自己平常還是蠻習慣用您所說的「暫存物件方法」去實作,但當有遇到效能調校的需求時,才會去調整寫法。

    畢竟自己現在,想到的速度永遠比不上忘記的速度啦。

    回覆刪除