jQuery源碼閱讀(九)---ready函數理解

在jQuery對象創建的時候,即init函數中,有處理這麼一種參數情況,當selector爲函數時,$(function(){ })表示跟$(document).ready(function(){ })是一樣的情況。
源碼是這樣的:

if(jQuery.isFunction(selector))
{
    //rootjQuery = $(document);
    return rootjQuery.ready(selector);
    //相當於調$(document).ready()方法。
}

$.fn.ready

上面的$(document).ready()相當於調jQuery的實例方法。我們來看看實例方法ready()的源碼:

ready: function(fn){
    jQuery.bindReady();
    readyList.add(fn);
    return this;
}

代碼只有短短三行,下來一個一個看調用的函數。

bindReady函數相當於監聽文檔加載完成的函數,即添加事件處理程序。

bindReady: function(){
    //文檔的狀態主要有五種情況:
    //UnInitialized:未開始加載
    //Loading:加載程序進行中,但是文件還沒有被解析;
    //Loaded:部分文件加載並解析,但是文檔對象模型(DOM)還暫未生效
    //Interactive:只對已加載的文件有效且DOM有效但是隻讀;
    //Complete: 文件已全部加載,表示加載成功。
    if ( document.readyState === "complete" ) {
        return setTimeout( jQuery.ready, 1 );      //處理IE的問題,它會提前觸發ready,所以延遲一下
        }
    if ( document.addEventListener ) {
        //添加事件處理程序,監聽DOMContentLoaded事件
        document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
        //爲什麼還要監聽load事件?因爲load事件是可以緩存的,有緩存的話,會先觸發load事件
        window.addEventListener( "load", jQuery.ready, false );
    } else if ( document.attachEvent ) {
        //IE事件處理函數
        document.attachEvent( "onreadystatechange", DOMContentLoaded );
        window.attachEvent( "onload", jQuery.ready );

        // 後面這部分暫時沒大看懂
        var toplevel = false;
        try {
            toplevel = window.frameElement == null;
        } catch(e) {}

        if ( document.documentElement.doScroll && toplevel ) {
            doScrollCheck();
        }
    }
}

上面的DOMContentLoaded事件處理函數如下:

if ( document.addEventListener ) {
    DOMContentLoaded = function() {
        document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false );
        jQuery.ready();
    };

} else if ( document.attachEvent ) {
    DOMContentLoaded = function() {
        // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
        if ( document.readyState === "complete" ) {
            document.detachEvent( "onreadystatechange", DOMContentLoaded );
            jQuery.ready();
        }
    };
}

可以看到,上面不論是哪種情況分支,都會最終調jQuery.ready()方法。也就是當文檔加載完成時,總是會觸發jQuery.ready函數

ready: function( wait ) {       
    //表示延遲事件處理函數,其中jQuery.readywait是由jQuery.holdReady函數控制的。
    if ( (wait === true && !--jQuery.readyWait) || (wait !== true && !jQuery.isReady) ) {
        if ( !document.body ) {
            return setTimeout( jQuery.ready, 1 );
        }

        //表示DOM加載完成,並記錄下狀態
        jQuery.isReady = true;

        if ( wait !== true && --jQuery.readyWait > 0 ) {
            return;
        }

        //執行回調函數,這部分函數是在底層模塊的回調部分實現的,後面看到的時候再梳理。
        readyList.fireWith( document, [ jQuery ] );

        //觸發ready函數
        //考慮到這種用法:$(document).on('ready', function(){})
        if ( jQuery.fn.trigger ) {
            jQuery( document ).trigger( "ready" ).off( "ready" );
        }
    }
}

以上是Ready部分的整體實現。下來再做一個簡單的梳理:

$(function(){
    console.log("Ready");
})

相當於調用$(document).ready(function(){ console.log("Ready") })
而裏面的回調函數function(){ console.log("Ready") } 是在頁面加載完成之後觸發,所以按照我們之前事件的思路,要先監聽事件,添加事件處理函數,然後在事件被觸發的時候才能去執行回調函數。而Ready的源碼也是這種思路。
首先bindReady函數監聽文檔加載完成事件,並定義事件處理函數complete
同時將回調函數加入到ReadyList中,這個是爲了管理多個回調函數。因爲jQuery中可以多次使用$(function(){ })

下來就是當頁面加載成功時,調用jQuery.ready() 函數,去分別將ReadyList中的回調函數觸發,這部分調的是ReadyList.fireWith() 方法,這個到後面看回調模塊時會再整理。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章