jQuery原始碼閱讀(九)---ready函式理解

鐺鐺鐺鐺Huan發表於2017-07-17

在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() 方法,這個到後面看回撥模組時會再整理。

相關文章