[譯] Jquery中 .bind() .live() .delegate() 和 .on() 之間的區別

could發表於2017-07-31

簡介

我瞭解到很多網頁開發者對jquery中的 .bind() .live() .delegate().on() 方法存在很多的疑惑。這些疑惑通常是關於它們之間真正的區別是什麼啊,什麼時候該使用它們啊。

在我們深入瞭解這些方法之前,我們先來一段常見的的HTML,作為我們編寫jquery示例方法使用的樣本。

<ul id="members" data-role="listview" data-filter="true">
    <!-- ... 其他li ... -->
    <li>
        <a href="detail.html?id=10">
            <h3>John Resig</h3>
            <p><strong>jQuery Core Lead</strong></p>
            <p>Boston, United States</p>
        </a>
    </li>
    <!-- ... 其他li ... -->
</ul>

使用Bind方法

.bind()方法將事件型別和一個事件處理函式直接註冊到了被選中的DOM元素中。這個方法被使用得最久,在此期間,它很好的解決了各種跨瀏覽器的問題。當使用它來連線事件處理函式時,它仍然非常簡潔,但是也存在著一些效能方面的問題,將在下面羅列出來。


/* .bind() 方法將事件型別和一個事件處理函式直接註冊到了被選中的DOM元素中。 
   .click() 方法只是.bind() 方法的簡寫。
*/

$( "#members li a" ).bind( "click", function( e ) {} ); 
$( "#members li a" ).click( function( e ) {} ); 

.bind()方法將會把事件處理函式連線到所有匹配的a標籤。這種方式並不好。這樣做的話,它不僅在所有匹配的元素中隱含地迭代附加事件處理函式,而且這些操作非常浪費(多餘),因為這些相同的事件處理函式是被一遍一遍的重複的新增到所有匹配的標籤上。

優點:

  • 適用於各種瀏覽器
  • 連線事件處理函式非常方便快捷
  • 可以使用 .click(), .hover()等簡寫方法來更方面地連線事件處理函式
  • 對於一個簡單的ID選擇器,使用.bind() 方法不僅可以很快地連線事件處理函式,而且當事件被觸發時, 事件處理函式幾乎是馬上就被呼叫了

缺點:

  • 這樣方法會將所有的事件處理函式附加到所有匹配的元素
  • 不可以動態地匹配相同選擇器的元素
  • 當操作大量匹配的元素時會有效能方面的問題
  • 附加操作是在前期完成的,這可能導致頁面載入時存在效能問題

使用Live方法

.live()方法使用了事件委託的概念來實施其所謂的“魔法”。你呼叫.live()方法的方式就像是呼叫.bind()方法那樣方便。然而在這表面之下,.live()方法與前者的實現方式大不相同。.live()方法將與事件處理函式關聯的選擇器和事件資訊一起附加到文件的根級元素(即document)。通過將事件資訊註冊到document上,這個事件處理函式將允許所有冒泡到document的事件呼叫它(例如委託型、傳播型事件)。一旦有一個事件冒泡到document元素上,Jquery會根據選擇器或者事件的後設資料來決定哪一個事件處理函式應該被呼叫,如果這個事件處理函式存在的話。這個額外的工作將會在使用者互動時對效能方面造成一定的影響,但是初始化註冊事件的過程相當地快。

/* 方法將與事件處理函式關聯的選擇器和事件資訊一起附加到文件的根級元素(即document) 
   ( "#members li a" & "click" ) */ 

$( "#members li a" ).live( "click", function( e ) {} );

.bind()這個例子與上面.bind()方法的例子對比的話有一個優點在於它僅僅把事件處理函式附加到document元素一次,而不是很多次。這樣不僅更快,而且還減少了效能的浪費。然而,使用這個方法也會帶來很多問題,下面將一一列出。

優點:

  • 所有的事件處理函式都只會被註冊一次,而不是像.bind()那樣進行多次註冊
  • .bind()方法升級到.live()方法非常方便,你僅需要將”bind”替代為”live”就可以了
  • 那些被動態新增到DOM的元素也將被神奇的匹配到,因為真實的事件資訊是被註冊到document元素上的
  • 你可以在文件載入完之前連線事件處理函式,這樣可以幫助你更好地利用你可能沒有用的時間

缺點:

  • 這個方法在Jquery 1.7以後的版本被棄用了,你應該在你的程式碼裡逐步放棄使用它
  • 使用這個方法時鏈式操作沒有得到正確的支援,可能會出現某些錯誤
  • 所做的匹配操作基本上沒用因為它只用於在document元素上註冊事件處理函式
  • 使用 event.stopPropogation() 方法將會沒用,因為事件總是已經被委託到了document元素上
  • 因為所有的選擇器或者事件資訊都被附加到document元素上了,所以一旦有一個事件要呼叫某個事件處理函式,Jquery會在一大堆儲存的後設資料中使用matchesSelector方法來決定哪一個事件處理函式將會被呼叫,如果這個函式有的話。
  • 因為你所連線的事件總是被委託到document上,所如果你的DOM的層級很深的話,這會導致一定的效能問題

使用Delegate方法

.delegate()方法與.live()方式實現方式相類似,它不是將選擇器或者事件資訊附加到document,而是讓你指定附加的元素。就像是.live()方法一樣,這個方法使用事件委託來正確地工作。

如果你跳過了前面關於 .live() 方法的介紹,你可能要回去重新看看它,因為這裡涉及到之前我所闡述的一些內部邏輯

/* .delegate() 方法會將選擇器和事件資訊 ( "li a" & "click" ) 附加到你指定的元素上 ( "#members" )。
*/

$( "#members" ).delegate( "li a", "click", function( e ) {} );

.delegate()方法十分強大。在上面這個例子中,與事件處理函式關聯的選擇器和事件資訊將會被附加到( #members” )這個元素上。這樣做比使用.live()高效多了,因為.live()方法總是將與事件處理函式關聯的選擇器和事件資訊附加到document元素上。另外,使用.delegate()方法解決許多其他問題。請參閱下方列出的詳細資訊。

優點:

  • 你可以選擇將選擇器或者事件資訊附加到指定的元素。
  • 匹配操作實際上在前面並沒有執行,而是用來註冊到指定的元素。
  • 鏈式操作可以得到正確的支援
  • Jquery仍然需要迭代這些選擇器或者事件資訊來匹配元素,不過因為你可以選擇哪一個元素作為根元素,所以篩選的量會大幅減少
  • 因為這項技術使用了事件委託機制,它可以匹配到被動態地新增到DOM的元素
  • 你可以在文件載入完之前連線事件處理函式

缺點:

  • .bind()方法不可以直接升級到.delegate()方法
  • Jquery仍然需要使用marchesSelector方法在附加到指定根元素的選擇器或者事件資訊中篩選決定哪一個事件處理函式會被呼叫。然而,附加到指定根元素的後設資料會比使用.live()方法的時候要小得多。
  • 當操作大量匹配的元素時會有效能方面的問題
  • 附加操作是在前期完成的,這可能導致頁面載入時存在效能問題

使用On方法

你知道嗎,在Jquery 1.7版本中.bind().live().delegate()方法只需要使用.on()方法一種方式來呼叫它們。當然.unbind().die().undelegate()方法也一樣。一下程式碼片段是從Jquery 1.7版本的原始碼中擷取出來的


bind: function( types, data, fn ) {
    return this.on( types, null, data, fn );
},
unbind: function( types, fn ) {
    return this.off( types, null, fn );
},

live: function( types, data, fn ) {
    jQuery( this.context ).on( types, this.selector, data, fn );
    return this;
},
die: function( types, fn ) {
    jQuery( this.context ).off( types, this.selector || "**", fn );
    return this;
},

delegate: function( selector, types, data, fn ) {
    return this.on( types, selector, data, fn );
},
undelegate: function( selector, types, fn ) {
    return arguments.length == 1 ? 
        this.off( selector, "**" ) : 
        this.off( types, selector, fn );
}

考慮到這一點,使用.on()方法看起來像以下方式一樣…


/* Jquery的 .bind() , .live() 和 .delegate() 方法只需要使用`.on()`方法一種方式來呼叫它們 */

// Bind
$( "#members li a" ).on( "click", function( e ) {} ); 
$( "#members li a" ).bind( "click", function( e ) {} ); 

// Live
$( document ).on( "click", "#members li a", function( e ) {} ); 
$( "#members li a" ).live( "click", function( e ) {} );

// Delegate
$( "#members" ).on( "click", "li a", function( e ) {} ); 
$( "#members" ).delegate( "li a", "click", function( e ) {} );

你可能注意到了,我如何使用.on()方法決定了它如何呼叫其他方法。你可以認為.on()方法被具有不同簽名的方法”過載“了,而這些方法實現了不同的事件繫結的連線方式。.on()方法的出現為API帶來了很多方面的一致性,並希望讓事情變得不那麼混亂。

優點:

  • 使各種事件繫結方法一致。
  • 因為在Jquery原始碼中.bind().live().delegate()方法實際上是呼叫了此方法,因此簡化了jQuery程式碼庫並刪除了一級重定向。
  • 這種方式仍然提供了使用.delegate()方法的優點,並且仍然提供對.bind()方法的支援,如果你需要的話。

缺點:

  • 給人帶來了一些疑惑,因為方法的實際執行方式將根據你如何呼叫方法而改變。

總結

如果你對不同的繫結事件方法有所迷惑,那麼不要擔心,因為API發展了一段時間了,有很多前人的經驗可以借鑑。也有很多人將這些方法視為魔法,不過一旦你瞭解了他們工作背後的原理,將幫助您瞭解如何更好地處理專案。
以下是這篇文章的精華所在…

  • 使用.bind()方法非常浪費效能因為它把同一個事件處理函式附加到了每一個匹配的元素上
  • 你應該停止使用.live()方法因為它被棄用了同時也會帶來很多問題
  • 使用.delegate()方法會給你帶來很多好處當你需要解決一些效能上的問題和對動態新增的元素作出處理
  • 新的.on()方法其實就是模擬.bind().live().delegate()實現的語法糖,具體取決於你如何呼叫它
  • 新的方向是使用新的.on()方法。先熟悉語法,並開始在你的所有的Jquery 1.7版本以上的專案使用它吧!

對於上面列舉的優點或者缺點,你有新的補充嗎?你最近開始使用.delegate()方法了嗎?你對新的.on()方法怎麼看呢?把你的想法寫到用評論告訴我吧!謝謝!


第一次翻譯,文章中可能會出現一些不通順的地方,希望得到大家的理解,畢竟我還是個學生啊!

原文連結

相關文章