溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

jQuery中的設計模式有哪些

發(fā)布時間:2021-01-07 09:29:33 來源:億速云 閱讀:220 作者:小新 欄目:web開發(fā)

這篇文章給大家分享的是有關jQuery中的設計模式有哪些的內(nèi)容。小編覺得挺實用的,因此分享給大家做個參考,一起跟隨小編過來看看吧。

jQuery中的設計模式有:1、組合模式,描述了一組對象可像單個對象一樣的對待;2、適配器模式;3、外觀模式;4、觀察者模式;5、迭代器模式;6、惰性初始模式;7、代理模式;8、建造者模式。

本教程操作環(huán)境:windows7系統(tǒng)、jquery3.5版本、Dell G3電腦。

推薦教程:jQuery教程

jQuery中的設計模式

jQuery是目前最流行的JavaScript DOM操作庫,它提供了一個在安全和跨瀏覽器的方式下與DOM交互的抽象層。有意思的是,這個庫也可以作為一個例子,來展示設計模式如何有效的創(chuàng)建既可讀又易用的API。

雖然在很多情況下,撰寫jQuery的主要貢獻者并沒有打算使用特定的模式,但是這些設計模式確實存在,而且對我們學習來說,非常有用。

組合模式

組合模式 描述了一組對象可像單個對象一樣的對待。

這允許我們能統(tǒng)一的處理單個對象或多個對象。這意味著無論是一個對象還是一千個對象我們都能以同樣的行為來處理。

在Jquery中,當我們在一個節(jié)點或多個節(jié)點上應用方法時,我們都能以相同的方式來選擇并返回JQuery對象。

下面這個演示我們將使用Jquery的選擇器。對單一元素(比如擁有唯一ID的元素)或擁有相同標簽或Class的一組元素添加名為active的class,對待它們使用上并無不同:

// 單一節(jié)點
$( "#singleItem" ).addClass( "active" );
$( "#container" ).addClass( "active" );

// 一組節(jié)點
$( "div" ).addClass( "active" );
$( ".item" ).addClass( "active" );
$( "input" ).addClass( "active" );

JQuery的addClass()實現(xiàn)中直接使用原生的for循環(huán)、Jquery的JQuery.each()、Jquery.fn.each來迭代一個集合以達到能同時處理一個或一組元素的目的。請看下面的例子:

 addClass: function( value ) {
  var classNames, i, l, elem,
    setClass, c, cl;

  if ( jQuery.isFunction( value ) ) {
    return this.each(function( j ) {
      jQuery( this ).addClass( value.call(this, j, this.className) );
    });
  }

  if ( value && typeof value === "string" ) {
    classNames = value.split( rspace );

    for ( i = 0, l = this.length; i < l; i++ ) {
      elem = this[ i ];

      if ( elem.nodeType === 1 ) {
        if ( !elem.className && classNames.length === 1 ) {
          elem.className = value;

        } else {
          setClass = " " + elem.className + " ";

          for ( c = 0, cl = classNames.length; c < cl; c++ ) {
            if ( !~setClass.indexOf( " " + classNames[ c ] + " " ) ) {
              setClass += classNames[ c ] + " ";
            }
          }
          elem.className = jQuery.trim( setClass );
        }
      }
    }
  }

  return this;
}

適配器模式

適配器模式 將一個對象或者類的接口翻譯成某個指定的系統(tǒng)可以使用的另外一個接口。

適配器基本上允許本來由于接口不兼容而不能一起正常工作的對象或者類能夠在一起工作.適配器將對它接口的調(diào)用翻譯成對原始接口的調(diào)用,而實現(xiàn)這樣功能的代碼通常是最簡的。

我們可能已經(jīng)用過的一個適配器的例子就是jQuery的jQuery.fn.css()方法,這個方法幫助規(guī)范了不同瀏覽器之間樣式的應用方式,使我們使用簡單的語法,這些語法被適配成為瀏覽器背后真正支持的語法:

// Cross browser opacity:
// opacity: 0.9;  Chrome 4+, FF2+, Saf3.1+, Opera 9+, IE9, iOS 3.2+, Android 2.1+
// filter: alpha(opacity=90);  IE6-IE8

// Setting opacity
$( ".container" ).css( { opacity: .5 } );

// Getting opacity
var currentOpacity = $( ".container" ).css('opacity');

將上面的代碼變得可行的相應的jQuery核心css鉤子在下面:

get: function( elem, computed ) {
 // IE uses filters for opacity
 return ropacity.test( (
       computed && elem.currentStyle ?
           elem.currentStyle.filter : elem.style.filter) || "" ) ?
   ( parseFloat( RegExp.$1 ) / 100 ) + "" :
   computed ? "1" : "";
},

set: function( elem, value ) {
 var style = elem.style,
   currentStyle = elem.currentStyle,
   opacity = jQuery.isNumeric( value ) ?
         "alpha(opacity=" + value * 100 + ")" : "",
   filter = currentStyle && currentStyle.filter || style.filter || "";

 // IE has trouble with opacity if it does not have layout
 // Force it by setting the zoom level
 style.zoom = 1;

 // if setting opacity to 1, and no other filters
 //exist - attempt to remove filter attribute #6652
 if ( value >= 1 && jQuery.trim( filter.replace( ralpha, "" ) ) === "" ) {

   // Setting style.filter to null, "" & " " still leave
   // "filter:" in the cssText if "filter:" is present at all,
   // clearType is disabled, we want to avoid this style.removeAttribute
   // is IE Only, but so apparently is this code path...
   style.removeAttribute( "filter" );

   // if there there is no filter style applied in a css rule, we are done
   if ( currentStyle && !currentStyle.filter ) {
     return;
   }
 }

 // otherwise, set new filter values
 style.filter = ralpha.test( filter ) ?
   filter.replace( ralpha, opacity ) :
   filter + " " + opacity;
}
};

外觀模式

正如我們早前在書中提過的, 沒面模式為一個龐大的(可能更復雜的)代碼結(jié)構(gòu)提供了一個更簡單的抽象接口。

門面在jQuery庫中能夠經(jīng)常見到,它們?yōu)殚_發(fā)者處理DOM節(jié)點,動畫或者令人特別感興趣的跨域Ajax提供了簡單的實現(xiàn)入口。

下面的代碼是jQuery $.ajax()方法的門面:

$.get( url, data, callback, dataType );
$.post( url, data, callback, dataType );
$.getJSON( url, data, callback );
$.getScript( url, callback );

這些方法背后真正執(zhí)行的代碼是這樣的:

// $.get()
$.ajax({
  url: url,
  data: data,
  dataType: dataType
}).done( callback );

// $.post
$.ajax({
  type: "POST",
  url: url,
  data: data,
  dataType: dataType
}).done( callback );

// $.getJSON()
$.ajax({
  url: url,
  dataType: "json",
  data: data,
}).done( callback );

// $.getScript()
$.ajax({
  url: url,
  dataType: "script",
}).done( callback );

更有趣的是,上面代碼中的門面實際上是它們自身具有的能力,它們隱藏了代碼背后很多復雜的操作。

這是因為jQuery.ajax()在jQuery核心代碼中的實現(xiàn)是一段不平凡的代碼,至少是這樣的。至少它規(guī)范了XHR(XMLHttpRequest)之間的差異而且讓我們能夠簡單的執(zhí)行常見的HTTP動作(比如:get、post等),以及處理延遲等等。

由于顯示與上面所講的門面相關的代碼將會占據(jù)整個章節(jié),這里僅僅給出了jQuery核心代碼中規(guī)劃化XHR的代碼:

// Functions to create xhrs
function createStandardXHR() {
  try {
    return new window.XMLHttpRequest();
  } catch( e ) {}
}

function createActiveXHR() {
  try {
    return new window.ActiveXObject( "Microsoft.XMLHTTP" );
  } catch( e ) {}
}

// Create the request object
jQuery.ajaxSettings.xhr = window.ActiveXObject ?
  /* Microsoft failed to properly
   * implement the XMLHttpRequest in IE7 (can't request local files),
   * so we use the ActiveXObject when it is available
   * Additionally XMLHttpRequest can be disabled in IE7/IE8 so
   * we need a fallback.
   */
  function() {
    return !this.isLocal && createStandardXHR() || createActiveXHR();
  } :
  // For all other browsers, use the standard XMLHttpRequest object
  createStandardXHR;
  ...

下面的代碼也處于實際的jQuery XHR(jqXHR)實現(xiàn)的上層,它是我們實際上經(jīng)常打交道的方便的門面:

// Request the remote document
   jQuery.ajax({
     url: url,
     type: type,
     dataType: "html",
     data: params,
     // Complete callback (responseText is used internally)
     complete: function( jqXHR, status, responseText ) {
       // Store the response as specified by the jqXHR object
       responseText = jqXHR.responseText;
       // If successful, inject the HTML into all the matched elements
       if ( jqXHR.isResolved() ) {
         // Get the actual response in case
         // a dataFilter is present in ajaxSettings
         jqXHR.done(function( r ) {
           responseText = r;
         });
         // See if a selector was specified
         self.html( selector ?
           // Create a dummy div to hold the results
           jQuery("

<div>

   ")
             // inject the contents of the document in, removing the scripts
             // to avoid any 'Permission Denied' errors in IE
             .append(responseText.replace(rscript, ""))

             // Locate the specified elements
             .find(selector) :

           // If not, just inject the full result
           responseText );
       }

       if ( callback ) {
         self.each( callback, [ responseText, status, jqXHR ] );
       }
     }
   });

   return this;
 }

</div>

觀察者模式

另一個我們之前提到過的模式就是觀察者(發(fā)布/訂閱)模式.這種模式下,系統(tǒng)中的對象可以在關注的事件發(fā)生的時候給其他對象發(fā)送消息,也可以被其他對象所通知。

jQuery核心庫很多年前就已經(jīng)提供了對于類似于發(fā)布/訂閱系統(tǒng)的支持,它們稱之為定制事件。

jQuery的早期版本中,可以通過使用jQuery.bind()(訂閱),jQuery.trigger()(發(fā)布),和jQuery.unbind()(取消訂閱)來使用這些定制事件,但在近期的版本中,這些都可以通過使用jQuery.on(),jQuery.trigger()和jQuery.off()來完成。

下面我們來看一下實際應用中的一個例子:

// Equivalent to subscribe(topicName, callback)
$( document ).on( "topicName" , function () {
    //..perform some behaviour
});

// Equivalent to publish(topicName)
$( document ).trigger( "topicName" );

// Equivalent to unsubscribe(topicName)
$( document ).off( "topicName" );

對于jQuery.on()和jQuery.off()的調(diào)用最后會經(jīng)過jQuery的事件系統(tǒng),與Ajax一樣,由于它們的實現(xiàn)代碼相對較長,我們只看一下實際上事件處理器是在哪兒以及如何將定制事件加入到系統(tǒng)中的:

jQuery.event = {

  add: function( elem, types, handler, data, selector ) {

    var elemData, eventHandle, events,
      t, tns, type, namespaces, handleObj,
      handleObjIn, quick, handlers, special;

    ...

    // Init the element's event structure and main handler,
    //if this is the first
    events = elemData.events;
    if ( !events ) {
      elemData.events = events = {};
    }
    ...

    // Handle multiple events separated by a space
    // jQuery(...).bind("mouseover mouseout", fn);
    types = jQuery.trim( hoverHack(types) ).split( " " );
    for ( t = 0; t < types.length; t++ ) {

      ...

      // Init the event handler queue if we're the first
      handlers = events[ type ];
      if ( !handlers ) {
        handlers = events[ type ] = [];
        handlers.delegateCount = 0;

        // Only use addEventListener/attachEvent if the special
        // events handler returns false
        if ( !special.setup || special.setup.call( elem, data,
        //namespaces, eventHandle ) === false ) {
          // Bind the global event handler to the element
          if ( elem.addEventListener ) {
            elem.addEventListener( type, eventHandle, false );

          } else if ( elem.attachEvent ) {
            elem.attachEvent( "on" + type, eventHandle );
          }
        }
      }

對于那些喜歡使用傳統(tǒng)的命名方案的人, Ben Alamn對于上面的方法提供了一個簡單的包裝,然后為我們提供了jQuery.publish(),jQuery.subscribe和jQuery.unscribe方法。我之前在書中提到過,現(xiàn)在我們可以完整的看一下這個包裝器。

(function( $ ) {

  var o = $({});

  $.subscribe = function() {
    o.on.apply(o, arguments);
  };

  $.unsubscribe = function() {
    o.off.apply(o, arguments);
  };

  $.publish = function() {
    o.trigger.apply(o, arguments);
  };

}( jQuery ));

在近期的jQuery版本中,一個多目的的回調(diào)對象(jQuery.Callbacks)被提供用來讓用戶在回調(diào)列表的基礎上寫新的方案。另一個發(fā)布/訂閱系統(tǒng)就是一個使用這個特性寫的方案,它的實現(xiàn)方式如下:

var topics = {};

jQuery.Topic = function( id ) {
    var callbacks,
        topic = id && topics[ id ];
    if ( !topic ) {
        callbacks = jQuery.Callbacks();
        topic = {
            publish: callbacks.fire,
            subscribe: callbacks.add,
            unsubscribe: callbacks.remove
        };
        if ( id ) {
            topics[ id ] = topic;
        }
    }
    return topic;
};

然后可以像下面一樣使用:

// Subscribers
$.Topic( "mailArrived" ).subscribe( fn1 );
$.Topic( "mailArrived" ).subscribe( fn2 );
$.Topic( "mailSent" ).subscribe( fn1 );

// Publisher
$.Topic( "mailArrived" ).publish( "hello world!" );
$.Topic( "mailSent" ).publish( "woo! mail!" );

//  Here, "hello world!" gets pushed to fn1 and fn2
//  when the "mailArrived" notification is published
//  with "woo! mail!" also being pushed to fn1 when
//  the "mailSent" notification is published.

// Outputs:
// hello world!
// fn2 says: hello world!
// woo! mail!

迭代器模式

迭代器模式中,迭代器(允許我們遍歷集合中所有元素的對象)順序迭代一個集合對象中的元素而無需暴漏其底層形式。

迭代器封裝了這種特別的迭代操作的內(nèi)部結(jié)構(gòu),就jQuery的jQuery.fn.each()迭代器來說,我們實際上可以使用jQuery.each()底層的代碼來迭代一個集合,而無需知道或者理解后臺提供這種功能的代碼是如何實現(xiàn)的。

這種模式可以被理解為門面模式的一種特例,在這里我們只處理與迭代有關的問題。

$.each( ["john","dave","rick","julian"] , function( index, value ) {
  console.log( index + ": "" + value);
});

$( "li" ).each( function ( index ) {
  console.log( index + ": " + $( this ).text());
});

這里我們可以看到jQuery.fn.each()的代碼:

// Execute a callback for every element in the matched set.
each: function( callback, args ) {
  return jQuery.each( this, callback, args );
}

在jQuery.each()方法后面的代碼提供了兩種迭代對象的方法:

each: function( object, callback, args ) {
  var name, i = 0,
    length = object.length,
    isObj = length === undefined || jQuery.isFunction( object );

  if ( args ) {
    if ( isObj ) {
      for ( name in object ) {
        if ( callback.apply( object[ name ], args ) === false ) {
          break;
        }
      }
    } else {
      for ( ; i < length; ) {
        if ( callback.apply( object[ i++ ], args ) === false ) {
          break;
        }
      }
    }

  // A special, fast, case for the most common use of each
  } else {
    if ( isObj ) {
      for ( name in object ) {
        if ( callback.call( object[ name ], name, object[ name ] ) === false ) {
          break;
        }
      }
    } else {
      for ( ; i < length; ) {
        if ( callback.call( object[ i ], i, object[ i++ ] ) === false ) {
          break;
        }
      }
    }
  }

  return object;
};

惰性初始模式

延遲初始化 是一種允許我們延遲初始化消耗資源比較大的進程,直到需要他們的時候(才初始化)。這其中的一個例子就是jQuery的.ready()方法,它在DOM節(jié)點加載完畢之后會執(zhí)行一個回調(diào)方法。

$( document ).ready( function () {

    //ajax請求不會執(zhí)行,直到DOM加載完成

    var jqxhr = $.ajax({
      url: "http://domain.com/api/",
      data: "display=latest&order=ascending"
    })
    .done( function( data ) ){
        $(".status").html( "content loaded" );
        console.log( "Data output:" + data );
    });

});

jQuery.fn.ready()底層是通過byjQuery.bindReady()來實現(xiàn)的, 如下所示:

bindReady: function() {
  if ( readyList ) {
    return;
  }

  readyList = jQuery.Callbacks( "once memory" );

  // Catch cases where $(document).ready() is called after the
  // browser event has already occurred.
  if ( document.readyState === "complete" ) {
    // Handle it asynchronously to allow scripts the opportunity to delay ready
    return setTimeout( jQuery.ready, 1 );
  }

  // Mozilla, Opera and webkit support this event
  if ( document.addEventListener ) {
    // Use the handy event callback
    document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );

    // A fallback to window.onload, that will always work
    window.addEventListener( "load", jQuery.ready, false );

  // If IE event model is used
  } else if ( document.attachEvent ) {
    // ensure firing before onload,
    // maybe late but safe also for iframes
    document.attachEvent( "onreadystatechange", DOMContentLoaded );

    // A fallback to window.onload, that will always work
    window.attachEvent( "onload", jQuery.ready );

    // If IE and not a frame
    // continually check to see if the document is ready
    var toplevel = false;

    try {
      toplevel = window.frameElement == null;
    } catch(e) {}

    if ( document.documentElement.doScroll && toplevel ) {
      doScrollCheck();
    }
  }
},

即使不直接在jQuery核心文件中使用,有些開發(fā)者通過一些插件也可能熟悉懶加載的概念,延遲加載和攬初始化一樣有效,它是一種在需要的時候(比如:當用戶瀏覽到了頁面底部的時候)才加載頁面數(shù)據(jù)的技術(shù)。最近幾年,這種模式已經(jīng)變得非常顯著并且現(xiàn)在可以再Twitter和Facebook的UI里面zhaoda。

代理模式

在我們需要在一個對象后多次進行訪問控制訪問和上下文,代理模式是非常有用處的。

當實例化一個對象開銷很大的時候,它可以幫助我們控制成本,提供更高級的方式去關聯(lián)和修改對象,就是在上下文中運行一個特別的方法。

在jQuery核心中,一個jQUery.proxy()方法在接受一個函數(shù)的輸入和返回一個一直具有特殊上下文的新的實體時存在。這確保了它在函數(shù)中的值時我們所期待的的值。

一個使用該模式的例子,在點擊事件操作時我們利用了定時器。設想我用下面的操作優(yōu)先于任何添加的定時器:

$( "button" ).on( "click", function () {
  // 在這個函數(shù)中,'this'代表了被當前被點擊的那個元素對象
  $( this ).addClass( "active" );
});

如果想要在addClass操作之前添加一個延遲,我們可以使用setTiemeout()做到。然而不幸的是這么操作時會有一個小問題:無論這個函數(shù)執(zhí)行了什么在setTimeout()中都會有個一個不同的值在那個函數(shù)中。而這個值將會關聯(lián)window對象替代我們所期望的被觸發(fā)的對象。

$( "button" ).on( "click", function () {
  setTimeout(function () {
    // "this" 無法關聯(lián)到我們點擊的元素
    // 而是關聯(lián)了window對象
    $( this ).addClass( "active" );
  });
});

為解決這類問題,我們使用jQuery.proxy()方法來實現(xiàn)一種代理模式。通過調(diào)用它在這個函數(shù)中,使用這個函數(shù)和我們想要分配給它的this,我們將會得到一個包含了我們所期望的上下文中的值。如下所示:

$( "button" ).on( "click", function () {

    setTimeout( $.proxy( function () {
        // "this" 現(xiàn)在關聯(lián)了我們想要的元素
        $( this ).addClass( "active" ); 
    }, this), 500);

    // 最后的參數(shù)'this'代表了我們的dom元素并且傳遞給了$.proxy()方法
});

jQuery代理方法的實現(xiàn)如下:

// Bind a function to a context, optionally partially applying any
 // arguments.
 proxy: function( fn, context ) {
   if ( typeof context === "string" ) {
     var tmp = fn[ context ];
     context = fn;
     fn = tmp;
   }

   // Quick check to determine if target is callable, in the spec
   // this throws a TypeError, but we will just return undefined.
   if ( !jQuery.isFunction( fn ) ) {
     return undefined;
   }

   // Simulated bind
   var args = slice.call( arguments, 2 ),
     proxy = function() {
       return fn.apply( context, args.concat( slice.call( arguments ) ) );
     };

   // Set the guid of unique handler to the same of original handler, so it can be removed
   proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++;

   return proxy;
 }

建造者模式

處理DOM時,我們常常想要去動態(tài)的構(gòu)建新的元素--這是一個會讓我們希望構(gòu)建的元素最終所包含的標簽,屬性和參數(shù)的復雜性有所增長的過程。

定義復雜的元素時需要特別的小心,特別是如果我們想要在我們元素標簽的字面意義上(這可能會亂成一團)擁有足夠的靈活性,或者取而代之去獲得更多面向?qū)ο舐肪€的可讀性。我們需要一種為我們構(gòu)建復雜DOM對象的機制,它獨立于為我們提供這種靈活性的對象本身,而這正是建造者模式為我們所提供的。

建造器使得我們僅僅只通過定義對象的類型和內(nèi)容,就可以去構(gòu)建復雜的對象,為我們屏蔽了明確創(chuàng)造或者展現(xiàn)對象的過程。

jQuery的美元標記為動態(tài)構(gòu)建新的jQuery(和DOM)對象提供了大量可以讓我們這樣做的不同的方法,可以通過給一個元素傳入完整的標簽,也可以是部分標簽還有內(nèi)容,或者使用jQuery來進行構(gòu)造:

$( '<div class="foo">bar</div>' );

$( '<p id="test">foo <em>bar</em></p>').appendTo("body");

var newParagraph = $( "<p />" ).text( "Hello world" );

$( "<input />" )
      .attr({ "type": "text", "id":"sample"});
      .appendTo("#container");

下面引用自jQuery內(nèi)部核心的jQuery.protoype方法,它支持從jQuery對象到傳入jQuery()選擇器的標簽的構(gòu)造。不管是不是使用document.createElement去創(chuàng)建一個新的元素,都會有一個針對這個元素的引用(找到或者被創(chuàng)建)被注入到返回的對象中,因此進一步會有更多的諸如as.attr()的方法在這之后就可以很容易的在其上使用了。

// HANDLE: $(html) -> $(array)
  if ( match[1] ) {
    context = context instanceof jQuery ? context[0] : context;
    doc = ( context ? context.ownerDocument || context : document );

    // If a single string is passed in and it's a single tag
    // just do a createElement and skip the rest
    ret = rsingleTag.exec( selector );

    if ( ret ) {
      if ( jQuery.isPlainObject( context ) ) {
        selector = [ document.createElement( ret[1] ) ];
        jQuery.fn.attr.call( selector, context, true );

      } else {
        selector = [ doc.createElement( ret[1] ) ];
      }

    } else {
      ret = jQuery.buildFragment( [ match[1] ], [ doc ] );
      selector = ( ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment ).childNodes;
    }

    return jQuery.merge( this, selector );

感謝各位的閱讀!關于“jQuery中的設計模式有哪些”這篇文章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,讓大家可以學到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI