温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

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

发布时间:2020-07-12 17:46:07 阅读:2328 作者:ztfriend 栏目:web开发
前端开发者测试专用服务器限时活动,0元免费领,库存有限,领完即止! 点击查看>>

看书时注意到下面两条语句的功效是相同的,

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

这个特殊写法就是用$()代替$(document).ready(),类似于(有差异)window.onload弹出个窗口:

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

查看jQuery1.8.3源代码,是这样封装的:

(function( windowundefined ) {      /*...jQuery源代码全部都在这里了...*/ })( window );  

下列语句把封装在内部的jQuery先赋给window.$,紧接着再赋给window.jQuery。这意味着在实际使用时window.$和window.jQuery是一回事。因为$这个符号只有1个字母,比jQuery短,所以更常用一些,但要注意到$非jQuery所独有,节约字母的代价是增加了命名冲突的风险。

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

下面是jQuery的初始化语句(注意到此时函数并未执行):

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

找到jQuery.fn的定义,这是一个对象,其中有一个叫init的函数元素: 

jQuery.fn = jQuery.prototype = {      constructor: jQuery,      init: function( selector, context, rootjQuery ) {          var match, elem, ret, doc;           // Handle $(""), $(null), $(undefined), $(false)          if ( !selector ) {              return this;          }           // Handle $(DOMElement)          if ( selector.nodeType ) {              this.context = this[0] = selector;              this.length = 1;              return this;          }  /*...以下省略...*

继续下去,init中有一段逻辑:

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

晕了晕了,rootjQuery的定义又回到了jQuery:

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

有点递归的意思了,嗯,就是递归。jQuery不仅仅是一个函数,而且还是一个递归函数。

如果调用jQuery时输入的是一个函数,例如文章开头提到的:

$(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函数的定义如下:

ready: function( fn ) {      // Add the callback      jQuery.ready.promise().done( fn );       return this;  }, 

阅读代码探究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函数代码如下(这下彻底晕了~~):

function() {       if ( list ) {           // First, we save the current length           var start = list.length;           (function add( args ) {               jQuery.each( args, function( _, arg ) {                   var type = jQuery.typearg );                   if ( type === "function" ) {                       if ( !options.unique || !self.has( arg ) ) { list.push( arg ); }                   } else if ( arg && arg.length && type !== "string" ) {                       // Inspect recursively add( arg );                   }               });           })( arguments );           // Do we need to add the callbacks to the           // current firing batch?           if ( firing ) {               firingLength = list.length;               // With memory, if we're not firing then               // we should call right away           } else if ( memory ) {               firingStart = start;        fire( memory );           }       }       return this;   } 

好在代码不长,看起来关键就在于fire函数了。嗯,找回一丝清醒了。在上面的done函数里面可以注意到使用了默认的arguments变量,将注入的函数push到了list数组。下面是fire函数:

fire = function( data ) {      memory = options.memory && data;      fired = true;      firingIndex = firingStart || 0;      firingStart = 0;      firingLength = list.length;      firing = true;      for ( ; list && firingIndex < firingLength; firingIndex++ ) {          if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {              memory = false; // To prevent further calls using add              break;          }      }      firing = false;      if ( list ) {          if ( stack ) {              if ( stack.length ) {                  fire( stack.shift() );              }          } else if ( memory ) {              list = [];          } else {              self.disable();          }      }  } 

可以看到代码中对list数组里面使用了apply。用iJs包调试可发现data[0]就是document对象,也就是说,调用$(myFunction)的结果是在document对象上执行了myFunction因为list是个数组,所以也就不难理解$()其实是多次输入,一次执行。

最后,回过头来阅读promise源代码,关于$()输入函数的执行时机的秘密就在这里了:

jQuery.ready.promise = function( obj ) {      if ( !readyList ) {           readyList = jQuery.Deferred();           // Catch cases where $(document).ready() is called after the browser event has already occurred.          // we once tried to use readyState "interactive" here, but it caused issues like the one          // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15          if ( document.readyState === "complete" ) {              // Handle it asynchronously to allow scripts the opportunity to delay ready              setTimeout( jQuery.ready, 1 );           // Standards-based browsers support DOMContentLoaded          } else if ( document.addEventListener ) {              // Use the handy event callback              document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );               // A fallback to window.              window.addEventListener( "load", jQuery.ready, false );           // If IE event model is used          } else {              // Ensure firing before               document.attachEvent( "onreadystatechange", DOMContentLoaded );               // A fallback to window.              window.attachEvent( "onload", jQuery.ready );               // If IE and not a frame              // continually check to see if the document is ready              var top = false;               try {                  top = window.frameElement == null && document.documentElement;              } catch(e) {}               if ( top && top.doScroll ) {                  (function doScrollCheck() {                      if ( !jQuery.isReady ) {                           try {                              // Use the trick by Diego Perini                              // http://javascript.nwbox.com/IEContentLoaded/                              top.doScroll("left");                          } catch(e) {                              return setTimeout( doScrollCheck, 50 );                          }                           // and execute any waiting functions                          jQuery.ready();                      }                  })();              }          }      }      return readyList.promise( obj );  }; 

从代码的注释中可以看到这段代码在消除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的大神们(以及其他类似框架的大神们),是他们的努力,让世界变得完美。

亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI

开发者交流群×