您好,登錄后才能下訂單哦!
這篇文章給大家分享的是有關jQuery中的設計模式有哪些的內容。小編覺得挺實用的,因此分享給大家做個參考,一起跟隨小編過來看看吧。
jQuery中的設計模式有:1、組合模式,描述了一組對象可像單個對象一樣的對待;2、適配器模式;3、外觀模式;4、觀察者模式;5、迭代器模式;6、惰性初始模式;7、代理模式;8、建造者模式。
本教程操作環境:windows7系統、jquery3.5版本、Dell G3電腦。
推薦教程:jQuery教程
jQuery中的設計模式
jQuery是目前最流行的JavaScript DOM操作庫,它提供了一個在安全和跨瀏覽器的方式下與DOM交互的抽象層。有意思的是,這個庫也可以作為一個例子,來展示設計模式如何有效的創建既可讀又易用的API。
雖然在很多情況下,撰寫jQuery的主要貢獻者并沒有打算使用特定的模式,但是這些設計模式確實存在,而且對我們學習來說,非常有用。
組合模式
組合模式 描述了一組對象可像單個對象一樣的對待。
這允許我們能統一的處理單個對象或多個對象。這意味著無論是一個對象還是一千個對象我們都能以同樣的行為來處理。
在Jquery中,當我們在一個節點或多個節點上應用方法時,我們都能以相同的方式來選擇并返回JQuery對象。
下面這個演示我們將使用Jquery的選擇器。對單一元素(比如擁有唯一ID的元素)或擁有相同標簽或Class的一組元素添加名為active的class,對待它們使用上并無不同:
// 單一節點 $( "#singleItem" ).addClass( "active" ); $( "#container" ).addClass( "active" ); // 一組節點 $( "div" ).addClass( "active" ); $( ".item" ).addClass( "active" ); $( "input" ).addClass( "active" );
JQuery的addClass()實現中直接使用原生的for循環、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; }
適配器模式
適配器模式 將一個對象或者類的接口翻譯成某個指定的系統可以使用的另外一個接口。
適配器基本上允許本來由于接口不兼容而不能一起正常工作的對象或者類能夠在一起工作.適配器將對它接口的調用翻譯成對原始接口的調用,而實現這樣功能的代碼通常是最簡的。
我們可能已經用過的一個適配器的例子就是jQuery的jQuery.fn.css()方法,這個方法幫助規范了不同瀏覽器之間樣式的應用方式,使我們使用簡單的語法,這些語法被適配成為瀏覽器背后真正支持的語法:
// 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; } };
外觀模式
正如我們早前在書中提過的, 沒面模式為一個龐大的(可能更復雜的)代碼結構提供了一個更簡單的抽象接口。
門面在jQuery庫中能夠經常見到,它們為開發者處理DOM節點,動畫或者令人特別感興趣的跨域Ajax提供了簡單的實現入口。
下面的代碼是jQuery $.ajax()方法的門面:
$.get( url, data, callback, dataType ); $.post( url, data, callback, dataType ); $.getJSON( url, data, callback ); $.getScript( url, callback );
這些方法背后真正執行的代碼是這樣的:
// $.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核心代碼中的實現是一段不平凡的代碼,至少是這樣的。至少它規范了XHR(XMLHttpRequest)之間的差異而且讓我們能夠簡單的執行常見的HTTP動作(比如:get、post等),以及處理延遲等等。
由于顯示與上面所講的門面相關的代碼將會占據整個章節,這里僅僅給出了jQuery核心代碼中規劃化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)實現的上層,它是我們實際上經常打交道的方便的門面:
// 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>
觀察者模式
另一個我們之前提到過的模式就是觀察者(發布/訂閱)模式.這種模式下,系統中的對象可以在關注的事件發生的時候給其他對象發送消息,也可以被其他對象所通知。
jQuery核心庫很多年前就已經提供了對于類似于發布/訂閱系統的支持,它們稱之為定制事件。
jQuery的早期版本中,可以通過使用jQuery.bind()(訂閱),jQuery.trigger()(發布),和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()的調用最后會經過jQuery的事件系統,與Ajax一樣,由于它們的實現代碼相對較長,我們只看一下實際上事件處理器是在哪兒以及如何將定制事件加入到系統中的:
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 ); } } }
對于那些喜歡使用傳統的命名方案的人, Ben Alamn對于上面的方法提供了一個簡單的包裝,然后為我們提供了jQuery.publish(),jQuery.subscribe和jQuery.unscribe方法。我之前在書中提到過,現在我們可以完整的看一下這個包裝器。
(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版本中,一個多目的的回調對象(jQuery.Callbacks)被提供用來讓用戶在回調列表的基礎上寫新的方案。另一個發布/訂閱系統就是一個使用這個特性寫的方案,它的實現方式如下:
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!
迭代器模式
迭代器模式中,迭代器(允許我們遍歷集合中所有元素的對象)順序迭代一個集合對象中的元素而無需暴漏其底層形式。
迭代器封裝了這種特別的迭代操作的內部結構,就jQuery的jQuery.fn.each()迭代器來說,我們實際上可以使用jQuery.each()底層的代碼來迭代一個集合,而無需知道或者理解后臺提供這種功能的代碼是如何實現的。
這種模式可以被理解為門面模式的一種特例,在這里我們只處理與迭代有關的問題。
$.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節點加載完畢之后會執行一個回調方法。
$( document ).ready( function () { //ajax請求不會執行,直到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()來實現的, 如下所示:
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核心文件中使用,有些開發者通過一些插件也可能熟悉懶加載的概念,延遲加載和攬初始化一樣有效,它是一種在需要的時候(比如:當用戶瀏覽到了頁面底部的時候)才加載頁面數據的技術。最近幾年,這種模式已經變得非常顯著并且現在可以再Twitter和Facebook的UI里面zhaoda。
代理模式
在我們需要在一個對象后多次進行訪問控制訪問和上下文,代理模式是非常有用處的。
當實例化一個對象開銷很大的時候,它可以幫助我們控制成本,提供更高級的方式去關聯和修改對象,就是在上下文中運行一個特別的方法。
在jQuery核心中,一個jQUery.proxy()方法在接受一個函數的輸入和返回一個一直具有特殊上下文的新的實體時存在。這確保了它在函數中的值時我們所期待的的值。
一個使用該模式的例子,在點擊事件操作時我們利用了定時器。設想我用下面的操作優先于任何添加的定時器:
$( "button" ).on( "click", function () { // 在這個函數中,'this'代表了被當前被點擊的那個元素對象 $( this ).addClass( "active" ); });
如果想要在addClass操作之前添加一個延遲,我們可以使用setTiemeout()做到。然而不幸的是這么操作時會有一個小問題:無論這個函數執行了什么在setTimeout()中都會有個一個不同的值在那個函數中。而這個值將會關聯window對象替代我們所期望的被觸發的對象。
$( "button" ).on( "click", function () { setTimeout(function () { // "this" 無法關聯到我們點擊的元素 // 而是關聯了window對象 $( this ).addClass( "active" ); }); });
為解決這類問題,我們使用jQuery.proxy()方法來實現一種代理模式。通過調用它在這個函數中,使用這個函數和我們想要分配給它的this,我們將會得到一個包含了我們所期望的上下文中的值。如下所示:
$( "button" ).on( "click", function () { setTimeout( $.proxy( function () { // "this" 現在關聯了我們想要的元素 $( this ).addClass( "active" ); }, this), 500); // 最后的參數'this'代表了我們的dom元素并且傳遞給了$.proxy()方法 });
jQuery代理方法的實現如下:
// 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時,我們常常想要去動態的構建新的元素--這是一個會讓我們希望構建的元素最終所包含的標簽,屬性和參數的復雜性有所增長的過程。
定義復雜的元素時需要特別的小心,特別是如果我們想要在我們元素標簽的字面意義上(這可能會亂成一團)擁有足夠的靈活性,或者取而代之去獲得更多面向對象路線的可讀性。我們需要一種為我們構建復雜DOM對象的機制,它獨立于為我們提供這種靈活性的對象本身,而這正是建造者模式為我們所提供的。
建造器使得我們僅僅只通過定義對象的類型和內容,就可以去構建復雜的對象,為我們屏蔽了明確創造或者展現對象的過程。
jQuery的美元標記為動態構建新的jQuery(和DOM)對象提供了大量可以讓我們這樣做的不同的方法,可以通過給一個元素傳入完整的標簽,也可以是部分標簽還有內容,或者使用jQuery來進行構造:
$( '<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內部核心的jQuery.protoype方法,它支持從jQuery對象到傳入jQuery()選擇器的標簽的構造。不管是不是使用document.createElement去創建一個新的元素,都會有一個針對這個元素的引用(找到或者被創建)被注入到返回的對象中,因此進一步會有更多的諸如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中的設計模式有哪些”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,讓大家可以學到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。