溫馨提示×

溫馨提示×

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

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

jQuery:理解$(document).ready()的特殊寫法

發布時間:2020-07-12 17:46:07 來源:網絡 閱讀:2331 作者:ztfriend 欄目:web開發

看書時注意到下面兩條語句的功效是相同的,

  1. $(function(){alert("hello!");});  
  2. $(document).ready(function(){alert("hello!");}); 

這個特殊寫法就是用$()代替$(document).ready(),類似于(有差異)window.onload彈出個窗口:

jQuery:理解$(document).ready()的特殊寫法 

查看jQuery1.8.3源代碼,是這樣封裝的:

  1. (function( window, undefined ) {  
  2.     /*...jQuery源代碼全部都在這里了...*/ 
  3. })( window );  

下列語句把封裝在內部的jQuery先賦給window.$,緊接著再賦給window.jQuery。這意味著在實際使用時window.$和window.jQuery是一回事。因為$這個符號只有1個字母,比jQuery短,所以更常用一些,但要注意到$非jQuery所獨有,節約字母的代價是增加了命名沖突的風險。

  1. // Expose jQuery to the global object  
  2. window.jQuery = window.$ = jQuery; 

下面是jQuery的初始化語句(注意到此時函數并未執行):

  1. // Define a local copy of jQuery  
  2. jQuery = function( selector, context ) {  
  3.     // The jQuery object is actually just the init constructor 'enhanced'  
  4.     return new jQuery.fn.init( selector, context, rootjQuery );  

找到jQuery.fn的定義,這是一個對象,其中有一個叫init的函數元素: 

  1. jQuery.fn = jQuery.prototype = {  
  2.     constructor: jQuery,  
  3.     init: function( selector, context, rootjQuery ) {  
  4.         var match, elem, ret, doc;  
  5.  
  6.         // Handle $(""), $(null), $(undefined), $(false)  
  7.         if ( !selector ) {  
  8.             return this;  
  9.         }  
  10.  
  11.         // Handle $(DOMElement)  
  12.         if ( selector.nodeType ) {  
  13.             this.context = this[0] = selector;  
  14.             this.length = 1;  
  15.             return this;  
  16.         }  
  17. /*...以下省略...*/ 

繼續下去,init中有一段邏輯:

  1. // HANDLE: $(function)  
  2. // Shortcut for document ready  
  3. else if ( jQuery.isFunction( selector ) ) {  
  4.     return rootjQuery.ready( selector );  

暈了暈了,rootjQuery的定義又回到了jQuery:

  1. // All jQuery objects should point back to these  
  2. rootjQuery = jQuery(document); 

有點遞歸的意思了,嗯,就是遞歸。jQuery不僅僅是一個函數,而且還是一個遞歸函數。

如果調用jQuery時輸入的是一個函數,例如文章開頭提到的:

  1. $(function(){alert("hello!");}); 

那么這個函數就會走到rootjQuery那里,再回到jQuery,執行jQuery(document).ready。而$與jQuery是一回事,這樣就解釋了$(inputFunction)可以代替$(document).ready(inputFunction)。

現在還不想結束此文,我的問題是$(document)做了什么?嗯,還是要進入到jQuery.fn.init,確認存在nodeType屬性,達到Handle $(DOMElement)”的目的。怎么Handle呢?具體就是把輸入參數(此時為document)賦值給this的context屬性,然后再返回this。也就是說,$(document)執行完了返回的還是jQuery,但是情況發生了變化,具體就是context屬性指向了輸入參數(此時為document)。暫時還不明白繞這么大個圈子為context(上下文)屬性賦值有何意義?

接下去的問題可能會是$(document).ready和window.onload的區別?提取ready函數的定義如下:

  1. ready: function( fn ) {  
  2.     // Add the callback  
  3.     jQuery.ready.promise().done( fn );  
  4.  
  5.     return this;  
  6. }, 

閱讀代碼探究promise是有點暈啊,想到自己的iJs工具包了,打印jQuery.ready.promise()如下:

    [Object] jQuery.ready.promise()
        |--[function] always
        |--[function] done
        |--[function] fail
        |--[function] pipe
        |--[function] progress
        |--[function] promise
        |--[function] state
        |--[function] then

進一步打印整理done函數代碼如下(這下徹底暈了~~):

  1. function() {   
  2.     if ( list ) {   
  3.         // First, we save the current length   
  4.         var start = list.length;   
  5.         (function add( args ) {   
  6.             jQuery.each( args, function( _, arg ) {   
  7.                 var type = jQuery.type( arg );   
  8.                 if ( type === "function" ) {   
  9.                     if ( !options.unique || !self.has( arg ) ) { list.push( arg ); }   
  10.                 } else if ( arg && arg.length && type !== "string" ) {   
  11.                     // Inspect recursively add( arg );   
  12.                 }   
  13.             });   
  14.         })( arguments );   
  15.         // Do we need to add the callbacks to the   
  16.         // current firing batch?   
  17.         if ( firing ) {   
  18.             firingLength = list.length;   
  19.             // With memory, if we're not firing then   
  20.             // we should call right away   
  21.         } else if ( memory ) {   
  22.             firingStart = start; 
  23.        fire( memory );   
  24.         }   
  25.     }   
  26.     return this;   

好在代碼不長,看起來關鍵就在于fire函數了。嗯,找回一絲清醒了。在上面的done函數里面可以注意到使用了默認的arguments變量,將注入的函數push到了list數組。下面是fire函數:

  1. fire = function( data ) {  
  2.     memory = options.memory && data;  
  3.     fired = true;  
  4.     firingIndex = firingStart || 0;  
  5.     firingStart = 0;  
  6.     firingLength = list.length;  
  7.     firing = true;  
  8.     for ( ; list && firingIndex < firingLength; firingIndex++ ) {  
  9.         if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {  
  10.             memory = false// To prevent further calls using add  
  11.             break;  
  12.         }  
  13.     }  
  14.     firing = false;  
  15.     if ( list ) {  
  16.         if ( stack ) {  
  17.             if ( stack.length ) {  
  18.                 fire( stack.shift() );  
  19.             }  
  20.         } else if ( memory ) {  
  21.             list = [];  
  22.         } else {  
  23.             self.disable();  
  24.         }  
  25.     }  

可以看到代碼中對list數組里面使用了apply。用iJs包調試可發現data[0]就是document對象,也就是說,調用$(myFunction)的結果是在document對象上執行了myFunction。因為list是個數組,所以也就不難理解$()其實是多次輸入,一次執行。

最后,回過頭來閱讀promise源代碼,關于$()輸入函數的執行時機的秘密就在這里了:

  1. jQuery.ready.promise = function( obj ) {  
  2.     if ( !readyList ) {  
  3.  
  4.         readyList = jQuery.Deferred();  
  5.  
  6.         // Catch cases where $(document).ready() is called after the browser event has already occurred.  
  7.         // we once tried to use readyState "interactive" here, but it caused issues like the one  
  8.         // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15  
  9.         if ( document.readyState === "complete" ) {  
  10.             // Handle it asynchronously to allow scripts the opportunity to delay ready  
  11.             setTimeout( jQuery.ready, 1 );  
  12.  
  13.         // Standards-based browsers support DOMContentLoaded  
  14.         } else if ( document.addEventListener ) {  
  15.             // Use the handy event callback  
  16.             document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );  
  17.  
  18.             // A fallback to window.  
  19.             window.addEventListener( "load", jQuery.ready, false );  
  20.  
  21.         // If IE event model is used  
  22.         } else {  
  23.             // Ensure firing before   
  24.             document.attachEvent( "onreadystatechange", DOMContentLoaded );  
  25.  
  26.             // A fallback to window.  
  27.             window.attachEvent( "onload", jQuery.ready );  
  28.  
  29.             // If IE and not a frame  
  30.             // continually check to see if the document is ready  
  31.             var top = false;  
  32.  
  33.             try {  
  34.                 top = window.frameElement == null && document.documentElement;  
  35.             } catch(e) {}  
  36.  
  37.             if ( top && top.doScroll ) {  
  38.                 (function doScrollCheck() {  
  39.                     if ( !jQuery.isReady ) {  
  40.  
  41.                         try {  
  42.                             // Use the trick by Diego Perini  
  43.                             // http://javascript.nwbox.com/IEContentLoaded/  
  44.                             top.doScroll("left");  
  45.                         } catch(e) {  
  46.                             return setTimeout( doScrollCheck, 50 );  
  47.                         }  
  48.  
  49.                         // and execute any waiting functions  
  50.                         jQuery.ready();  
  51.                     }  
  52.                 })();  
  53.             }  
  54.         }  
  55.     }  
  56.     return readyList.promise( obj );  
  57. }; 

從代碼的注釋中可以看到這段代碼在消除bug的過程中還是頗費了些心思的。查看其中一個網址http://bugs.jquery.com/ticket/12282#comment:15,是關于IE9/10的一個bug(document ready is fired too early on IE 9/10),好在已經解決。

繞了這么多彎子,整個事情看起來就是這樣,如果每一個瀏覽器都能有document.readyState === "complete",就簡單了。再看到$(),要感謝編寫jQuery的大神們(以及其他類似框架的大神們),是他們的努力,讓世界變得完美。

向AI問一下細節

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

AI

亚洲午夜精品一区二区_中文无码日韩欧免_久久香蕉精品视频_欧美主播一区二区三区美女