美文网首页我爱编程
jQuery-v2.0.3源码浅析02-$(function()

jQuery-v2.0.3源码浅析02-$(function()

作者: 赠前端 | 来源:发表于2018-03-26 15:50 被阅读0次

    前言

    上篇文章讲解了jQuery的链式操作,接下来我们来看一下在jQuery中使用最频繁的代码:

    $(function(){
      ***
    })
    

    这段代码的意思是指当页面dom加载完毕之后会自动调用这个匿名函数。
    我们来看一下源码是怎么实现的

    源码

    /**源码90行**/
    completed = function() {
      document.removeEventListener( "DOMContentLoaded", completed, false );
      window.removeEventListener( "load", completed, false );
      jQuery.ready();
    };
    /**源码96行**/
    jQuery.fn = jQuery.prototype = {
      /**源码101行**/
      init: function( selector, context, rootjQuery ) {
        ***
        /**源码184行**/
        } else if ( jQuery.isFunction( selector ) ) {
          return rootjQuery.ready( selector );
        }
      },
      /**源码239行**/
      ready: function( fn ) {
        // Add the callback
        jQuery.ready.promise().done( fn );
        return this;
      }
    }
    /**源码348行**/
    jQuery.extend({
      /**源码381行**/
      ready: function( wait ) {
        ***
        /**源码397行**/
        readyList.resolveWith( document, [ jQuery ] );
      }
    }
    /**源码818行**/
    jQuery.ready.promise = function( obj ) {
      if ( !readyList ) {
        readyList = jQuery.Deferred();
        if ( document.readyState === "complete" ) {
          setTimeout( jQuery.ready );
        } else {
          document.addEventListener( "DOMContentLoaded", completed, false );
          window.addEventListener( "load", completed, false );
        }
      }
      return readyList.promise( obj );
    };
    /**源码865行**/
    rootjQuery = jQuery(document);
    

    我们从上篇文章已经得知$()最终创建的是 jQuery.fn.init 的实例。
    我们调用$(function(){})。最终init的第一个参数selector就是我们传入的回调函数。
    在源码184行可以看到jQuery通过判断selector是函数又调用了rootjQuery.ready( selector )。
    从源码865行可以得知rootjQuery 其实就是 jQuery(document) 等价于 $(document)。
    这个时候我们就可以猜测jQuery原型下面应该挂载了一个ready方法。
    当然我们的猜测是正确的 我们可以通过源码239行看到这个ready方法。
    最终调用了jQuery.ready.promise().done( fn )。
    我们可以从818行找到jQuery.ready.promise函数,可以看到函数最终return了一个promise对象(jQuery自定义的promise非es6中自带的Promise)。
    最终调用了jQuery.ready();

    相信很多刚开始看的同学可能已经看糊涂了(我一开始也搞得很迷糊),不要慌,接下来我们再来重新理一下代码的执行过程和逻辑,不懂的代码就要不断的去重新整理重新阅读。或者使用调试工具跑一遍看看。

    $(function(){}) -> 
    return new jQuery.fn.init( selector, context, rootjQuery );->
    return rootjQuery.ready( selector );->//到这一步为止jQuery就进行了一些简单判断相信大家都能看得懂
    jQuery.ready.promise() //执行该函数时候内部进行了一些判断我们来一起看一下
    

    函数
    1、第一步先判断readList是否存在,可以通过源码26行看到定义了readList并没有赋值,所以第一次执行该函数的时候条件成立。然后给readyList赋值为一个 Deferred 对象(后续会介绍Deferred的实现原理)

    2、接着又判断了document.readyState === "complete" 这一步的作用是,有可能当执行到这段代码的时候dom已经加载完毕了,这个时候document.readyState的值为‘complete’。这个时候其实我们就可以直接调用回调函数了,这里jQuery利用setTimeout调用了jQuery.ready。else 就是来处理当dom还没加载完毕的逻辑。我们发现jQuery监听了document 的 ‘DOMContentLoaded’ 和 window的‘load’ 两个事件监听(兼容IE老版本有的时候load事件会比DOMContentLoaded来的快)。jQuery为了确保第一时间执行回调函数所以监听了两个事件。然后最后返回promise对象。

    3、接下里我们来看下completed函数,先清除了两个监听事件(防止重复调用)然后再最终也调用了jQuery.ready

    4、最后我们来看一下jQuery.ready函数,先忽略其他代码看到397行,我们可以看到readyList.resolveWith来最终执行了回调函数。也就是自己传入的匿名函数。当然这块匿名函数是通过
    jQuery.ready.promise().done( fn );传入的。

    所以最终可以把

    解释

    可能部分同学没有使用过jQuery的Deferred方法,接下来简单介绍一下Deferred方法的使用,后续再对源码进行详细剖析。

    var def = $.Deferred()
    var promise = def.promise();
    promise.done(function(){
      console.log(1);
    });
    setTimeout(function(){
      def.resolveWith( document );
    }, 3000)
    

    这段代码会在3秒之后在控制台打印出1。
    dfe大家可以先理解成改状态用的,promise大家可以理解成添加回调方法用的。(后续介绍延迟对象的时候会详解)
    done可以看成是添加成功回调函数的方法(对应也有添加失败的方法)。在resolve或者resolveWith会依次执行(resolve和resolveWith的区别是resolveWith可以改变函数执行时候的this指向,resolve最终调用的其实还是resolveWith)。
    resolveWith其实就是改变状态(可以看成会循环触发done里面添加的函数)

    其他

    /**源码400行**/
    if ( jQuery.fn.trigger ) {
      jQuery( document ).trigger("ready").off("ready");
    }
    

    //以上代码处理的是通过事件形式绑定的回调,例如:

    $(document).on('ready', function(){
      ***
    })
    

    再看jQuery.ready方法的其他判断代码:

    /**源码372行**/
    holdReady: function( hold ) {
      if ( hold ) {
        jQuery.readyWait++;
      } else {
        jQuery.ready( true );
      }
    }
    /**源码384行**/
    if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
      return;
    }
    jQuery.isReady = true;
    if ( wait !== true && --jQuery.readyWait > 0 ) {
      return;
    }
    

    以上代码处理的是配合$.holdReady(true)来使用的,有的时候我们想在dom加载完毕之后不立即触发回调函数可以通过调用该代码来实现。
    其实底层实现就是通过类似计数器方式来实现,利用isReady来存储dom加载状态,计数器的变量名称定义为readyWait
    调用$.holdReady(true)就把计数器+1,不传参数或者传false就调用jQuery.ready( true )来让计数器-1,当计数器减到0就执行回调函数。

    相关文章

      网友评论

        本文标题:jQuery-v2.0.3源码浅析02-$(function()

        本文链接:https://www.haomeiwen.com/subject/hpxrcftx.html