zoukankan      html  css  js  c++  java
  • jquery源码 DOM加载

    jQuery版本:2.0.3

    DOM加载有关的扩展

    • isReady:DOM是否加载完(内部使用) 
    • readyWait:等待多少文件的计数器(内部使用)
    • holdReady():推迟DOM触发
    • ready():准备DOM触发。
    • jQuery.ready.promise=function(){};  监听DOM的异步操作(内部使用)

    一、$(function(){})和原生window.onload的关系

    这个在面试中也是经常会被问到的。从下面几个角度来分析一下它们的区别

    1、执行时机

    页面加载,先加载节点,再加载文件,比如img文件,flash等。

    $(function(){})DOM加载完执行。可能DOM元素关联的东西并没有加载完。

    window.onload等节点和文件都加载完执行。

    对应的事件监听

    jQuery用的是DOMContentLoaded事件。

    DOMDContentLoaded:原生DOM加载事件,这个事件触发代表DOM加载完了。

    我之前写过一篇文章的页面加载时间分析里也有提到。

    onload对应的是load事件。

    2、个数

    window.onload不能写多个,后面的会覆盖前面的。

    $(function(){})可以写多个。都会执行 。

    3、简化写法

    $(function(){})是$(document) .ready(function(){});的简化写法。

    window.onload没有简化写法。

    二、jQurey如何实现DOM ready的

    jQuery直接调用DOMContentLoaded来实现DOM的ready。但是DOMContentLoaded和onLoad一样,浏览器只执行一次,jQuery用什么判断是否已经执行过呢?document.readyState就是判断这个的依据。

    readyState是document的属性,总共有3个值:

    • loading:文档正在加载中
    • interactive:文档已经加载完成,正在进行css和图片等资源的加载
    • complete:文档的所以资源加载完成

    判断完之后如何回调呢?就是用Promise。jQuery通过new一个$.Deferred(promise)对象来实现对DOM的ready的回调,在DOMContentLoaded中将这个promise给resolve掉,这样就执行了之前注册的回调函数,同时后面新注册的回调也会立刻执行。

    但是在调用promise之前,jQuery执行了一次setTimeout,因为jQuery.Promise是不会产生异步的,这和标准的promise规范是不一样的,所有jQuery自己又手动做了一次setTimeout来实现异步。这样使得无论使用在DOM的ready之前注册的回调还是之后注册的回调都会在异步中执行。

    三、源码整体实现逻辑

    $(fn)==>new一个 jQuery.fn.init(fn)==>返回$(document).ready( fn)。也就是说

    • $(function(){}) =》
    • 调用$(document).ready(function(){})=》
    • 相当于$().ready(fn)实例方法=》
    • 调用的jQuery.ready.promise().done(fn)=》
    • jQuery.ready.promise中不管if还是else最终都是调用jQuery.ready(),并返回promise=》
    • jQuery.ready()里面重点是readyList.resolveWith( document, [ jQuery ] );已完成。至此DOM加载完毕就可以调用fn了。

    四、实现细节

    1、重点是:jQuery.ready.promise()方法【巧妙

    如果DOM已经加载完成了,调用jQuery.ready()这个工具方法;

    如果DOM没加载完,监听DOMContentLoaded事件和load事件,等事件发生时回调completed(),最终也是调用jQuery.ready()这个工具方法;

    复制代码
    var    // A central reference to the root jQuery(document)
        rootjQuery,
    
        // The deferred used on DOM ready
        readyList;
    
    jQuery.ready.promise = function( obj ) {
        if ( !readyList ) { //第一次readyList为空可以进来,后续就进不来if了,只执行一次
    
            readyList = jQuery.Deferred(); //第一步,创建延迟对象
            if ( document.readyState === "complete" ) { //DOM加载完成的标志就是document.readyState为complete,如果DOM已经加载好了就直接调工具方法jQuery.ready。
                setTimeout( jQuery.ready );//加定时器是为了兼容IE
            } else {//DOM没有加载完检测,即检测了DOMContentLoaded事件,也检测了load事件;最终走回调completed函数
                // Use the handy event callback
                document.addEventListener( "DOMContentLoaded", completed, false );
                // A fallback to window.onload, that will always work
                window.addEventListener( "load", completed, false ); //因为火狐浏览器会缓存load事件,为了第一时间相应所以对load也监听了
            }
        }
        return readyList.promise( obj );
    };
    复制代码

    completed回调函数如下,最终调用的也是jQuery.ready()。 

    复制代码
        // The ready event handler and self cleanup method
        completed = function() {
        //不管是DOMContentLoaded事件还是load发生,都会取消2个事件监听
        //jQuery.ready()只会触发一次
            document.removeEventListener( "DOMContentLoaded", completed, false );
            window.removeEventListener( "load", completed, false );
            jQuery.ready();
        };
    复制代码

    2、jQuery.ready()工具方法做了些什么 

    先做个测试:

       $(function(arg){
           alert(this); //[object HTMLDocument]
           alert(arg); //jQuery函数
       })

    再做个测试:

    除了$(function(){});$(document).ready(function(){}),也可以把ready当做事件来处理如下。

    <script>
    $(document).on("ready",function(){
        alert(123); //123
    });
    </script>

    之所以会自动弹出123,是因为在jQuery源码中用这句话jQuery( document ).trigger("ready").off("ready");来主动触发了ready事件,触发后再取消掉。

    复制代码
    // Handle when the DOM is ready
    ready: function( wait ) { //参数和holdReady有关
    
        // Abort if there are pending holds or we're already ready
        if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
            return;
        }
    
        // Remember that the DOM is ready
        jQuery.isReady = true;
    
        // If a normal DOM Ready event fired, decrement, and wait if need be
        if ( wait !== true && --jQuery.readyWait > 0 ) {
            return;
        }
        //第一步看这里重点,resolveWith改变状态的时候传参了,给done中方法fn传入了参数,
        //document是fn的this指向,jQuery是参数
        // If there are functions bound, to execute
        readyList.resolveWith( document, [ jQuery ] );
        //跟主动触发有关
        // Trigger any bound ready events
        if ( jQuery.fn.trigger ) {
            jQuery( document ).trigger("ready").off("ready");
        }
    },
    复制代码

    跟ready的参数有关的有一个holdReady()。

    先做个测试

    $.holdReady(true);
    $(function () {
        alert(123); //调用了holdReady并传参true,就不能弹出123了
    });

    可以推迟,也可以释放推迟,释放了以后就可以触发了。

    $.holdReady(true);
    $.holdReady(false);
    $(function () {
        alert(123); //释放了holdReady,就弹出123
    });

    这个有什么用?

    比如:

    复制代码
    $.getScript('js/a.js',function(){ //异步加载,不会影响后续代码执行。可能会产生一个问题,alert(2)先执行了a.js还没有加载完
    
    })
    
    $(function () {
        alert(2);//先弹2,后弹出1
    });
    复制代码

    很多时候引入外部文件的时候,都想等外部文件或者插件加载完,再去触发操作,操作可能用到a.js。现在这个顺序不对,会出问题。

    怎样解决这个问题?用holdReady()方法。

    复制代码
        $.holdReady(true); //在这里先hold住
        $.getScript('js/a.js', function () { //异步加载,不会影响后续代码执行。可能会产生一个问题,alert(2)先执行了a.js还没有加载完
            $.holdReady(false); //加载完释放,不hold了就可以弹2了
        })
    
        $(function () {
            alert(2);//先弹2,后弹出1
        });
    复制代码

    再深入一点,holdReady() 要针对的文件可能不止一个,有很多个,所以要等所有的文件都加载完再执行,所以源码中有一个计数的readyWait。

    源码中定义了一个等待栈变量——readyWait,每次执行$.holdReady(true)都会增加压栈,而每次$.holdReady()执行都会弹栈,等空栈的时候就执行jQuery.ready函数,即将promise给resolve掉。

    复制代码
    // Is the DOM ready to be used? Set to true once it occurs.
    isReady: false,
    // A counter to track how many items to wait for before
    // the ready event fires. See #6781
    readyWait: 1,
    
    //第二步看这里
    //holdReady推迟DOM的触发
    // Hold (or release) the ready event
    holdReady: function( hold ) {
        if ( hold ) {
            jQuery.readyWait++;//hold为真,让readyWait加加处理
        } else {
            jQuery.ready( true );
        }
    },
    
    // Handle when the DOM is ready
    ready: function( wait ) { //参数和holdReady有关
    
        // Abort if there are pending holds or we're already ready
        //readyWait为0时就不用hold了,执行后面的操作
        if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { 
            return;
        }
    
        // Remember that the DOM is ready
        jQuery.isReady = true; //默认是false
    
        // If a normal DOM Ready event fired, decrement, and wait if need be
        if ( wait !== true && --jQuery.readyWait > 0 ) {
            return;
        }
        //第一步看这里重点,resolveWith改变状态的时候传参了,给done中方法fn传入了参数,
        //document是fn的this指向,jQuery是参数
        // If there are functions bound, to execute
        readyList.resolveWith( document, [ jQuery ] );
        //跟主动触发有关
        // Trigger any bound ready events
        if ( jQuery.fn.trigger ) {
            jQuery( document ).trigger("ready").off("ready");
        }
    },
    复制代码

    刚开始看源码,很多地方理解也不到位,解释可能也不清楚。

  • 相关阅读:
    网站安全编程 黑客入侵 脚本黑客 高级语法入侵 C/C++ C# PHP JSP 编程
    【算法导论】贪心算法,递归算法,动态规划算法总结
    cocoa2dx tiled map添加tile翻转功能
    8月30日上海ORACLE大会演讲PPT下载
    【算法导论】双调欧几里得旅行商问题
    Codeforces Round #501 (Div. 3) B. Obtaining the String (思维,字符串)
    Codeforces Round #498 (Div. 3) D. Two Strings Swaps (思维)
    Educational Codeforces Round 89 (Rated for Div. 2) B. Shuffle (数学,区间)
    洛谷 P1379 八数码难题 (BFS)
    Educational Codeforces Round 89 (Rated for Div. 2) A. Shovels and Swords (贪心)
  • 原文地址:https://www.cnblogs.com/libin-1/p/6860981.html
Copyright © 2011-2022 走看看