开发者社区> 技术小胖子> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

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

简介:
+关注继续查看

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


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

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

 

查看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的大神们(以及其他类似框架的大神们),是他们的努力,让世界变得完美。






 本文转自 hexiaini235 51CTO博客,原文链接:http://blog.51cto.com/idata/1119589,如需转载请自行联系原作者


版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
jquery $(document).ready() 与window.onload的区别

Jquery中$(document).ready()的作用类似于传统JavaScrip

1352 0
jquery $(document).ready() 与window.onload的区别
Jquery中$(document).ready()的作用类似于传统JavaScript中的window.onload方法,不过与window.onload方法还是有区别的。 1.执行时间        >    alert(“text1”); }; window.onload =>      // 编写代码 } ————————————————————
929 0
jQuery插件开发的五种形态[转]
这篇文章主要介绍了jQuery插件开发的五种形态小结,具体的内容就是解决javascript插件的8种特征,非常的详细。 关于jQuery插件的开发自己也做了少许研究,自己也写过多个插件,在自己的团队了也分享过一次关于插件的课。开始的时候整觉的很复杂的代码,现在再次看的时候就清晰了许多。这里我把我
2703 0
【精心挑选】10款基于 jQuery 的图片360度旋转插件
  之前的文章向大家分享了实现网站功能的各种优秀的 jQuery 插件,今天这篇文章向大家推荐10款基于 jQuery 的图片360度旋转插件,同时还有非常详细的制作教程可以学习和参考。图片旋转展示是一种非常有趣的技术,通过把多张图片合并在一起让你可以实现360度浏览,这种效果常用于旅游网站或者酒店网站,用于展示风景或者房间的内景。
1020 0
40款非常棒的 jQuery 插件和制作教程(系列一)
jQuery 是一个非常优秀的 JavaScript 框架,在现在的 Web 开发项目中扮演着重要角色。jQuery 使用简单灵活,同时还有许多成熟的插件可供选择,它可以帮助你在项目中加入一些非常好的效果,让网站有更好的可用性和用户体验。
820 0
10个最佳jQuery Lightbox效果插件收集
  大家都很喜欢 Lightbox 弹框效果,这种效果在很多地方都很有用。而原始的 Lightbox 脚本已经被无数次的克隆到了所有的流行 Javascript 库中。本文特别收集了10个最佳的Lightbox效果插件,所以收藏本文吧,不定什么时候你就用到了…… 您可能感兴趣的相关文章 ...
942 0
21114
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载
冬季实战营第三期:MySQL数据库进阶实战
立即下载