常常用jQuery類庫或其餘類庫中的ready方法,有時候想一想它們究竟是怎麼實現的,可是看了一下jQuery中的源碼,涉及到的模塊比較多,(水平有限)代碼比較難看懂;本身結合了一些書籍內容,總結一下。css
先說一下ready函數的實現思路:瀏覽器
變量ready經過表達式賦值,右側爲一個自執行匿名函數,在這個匿名函數中,首先爲各個瀏覽器的事件綁定處理函數,併爲isReady賦值(根據事件異步處理程序來肯定),而後返回一個傳參閉包,在閉包中,主要判斷isReady值來執行操做,若是dom結構準備就緒(isReady === true),執行回調,不然將回調加入到要執行的隊列(funs)中,待事件處理程序執行時,循環遍歷隊列(funs),並依次執行隊列中的函數,執行完隊列中的函數後,還須要清除隊列(funs = null)。閉包
var ready = (function(){ var isReady = false, funs = []; function handle (e) { if ( isReady ) { return; } if ( e.type === 'readystatechange' && (document.readyState !== 'interactive' && document.readyState !== 'complete') ) { return; } for ( var i = 0; i < funs.length; i++ ) { funs[i].call(document); } isReady = true; funs = null; } if ( document.addEventListener ) { document.addEventListener( 'DOMContentLoaded', handle, false ); document.addEventListener( 'readystatechange', handle, false ); document.addEventListener( 'load', handle, false ); } else if ( document.attachEvent ) { document.attachEvent( 'onreadystatechange', handle ); document.attachEvent( 'onload', handle ); } return function ready (callback) { if ( isReady ) { callback.call(document); } else { funs.push(callback); } }; }());
PS:app
該函數代碼參照於權威指南書籍,惟一不一樣的是,多加了一個判斷document.readyState !== 'interactive'dom
if ( e.type === 'readystatechange' && (document.readyState !== 'interactive' && document.readyState !== 'complete') ) { return; }
在各個瀏覽器中交互和完成狀態出現順序並不能保證一致,這取決於瀏覽器及頁面的內容,多加了這個判斷document.readyState !== 'interactive'的話,
意思是無論哪一個階段先出現,代碼都能更早的執行。異步
參照了jQuery源碼,寫了一個type函數,返回參數類型。 函數
/** * * 判斷參數類型 * createTime: 2013/9/18 * */ function type (obj) { var classTypes, objectTypes; if ( obj == null ) { return String(obj); } classTypes = {}; objectTypes = ('Boolean Number String Function Array Date RegExp Object Error').split(' '); for ( var i = 0, len = objectTypes.length; i < len; i++ ) { classTypes[ '[object ' + objectTypes[i] + ']' ] = objectTypes[i].toLowerCase(); } if ( typeof obj === 'object' || typeof obj === 'function' ) { var key = Object.prototype.toString.call(obj); return classTypes[key]; } return typeof obj; }
// css按需加載 function loadCss (cssUrl, callback) { var elem, bl, isExecuted = false; // 防止在ie9中,callback執行兩次 if ( cssUrl == null ) { return String(cssUrl); } elem = document.createElement('link'), elem.rel = 'stylesheet'; if ( type(callback) === 'function' ) { bl = true; } // for ie function handle() { if ( elem.readyState === 'loaded' || elem.readyState === 'complete' ) { if (bl && !isExecuted) { callback(); isExecuted = true; } elem.onreadystatechange = null; } } elem.onreadystatechange = handle; // for 非ie if (bl && !isExecuted) { elem.onload = callback; isExecuted = true; } elem.href = cssUrl; document.getElementsByTagName('head')[0].appendChild(elem); } // js按需加載 function loadScript(scriptUrl, callback) { var elem, bl, isExecuted = false; // 防止在ie9中,callback執行兩次 if (scriptUrl == null) { return String(fn); } elem = document.createElement('script'); if ( type(callback) === 'function' ) { bl = true; } // for ie function handle(){ var status = elem.readyState; if (status === 'loaded' || status === 'complete') { if (bl && !isExecuted) { callback(); isExecuted = true; } elem.onreadystatechange = null; } } elem.onreadystatechange = handle; // for 非ie if (bl && !isExecuted) { elem.onload = callback; isExecuted = true; } elem.src = scriptUrl; document.getElementsByTagName('head')[0].appendChild(elem); }
PS: 在判斷link,script元素是否加載完畢,主要依靠load事件;而在ie9如下瀏覽器中,並無load事件,ie爲它們都添加了一個readystatechange事件,經過判斷
元素的readyState狀態肯定元素是否已經加載完畢;而奇怪的是,在ie9(還可能存在其餘瀏覽器版本)中,元素既有load事件又有readystatechange事件,所以在代碼中添加了一個變量isExecuted,若是執行過回調,那麼就再也不執行,避免回調執行兩次。spa
loadCss('http://a.tbcdn.cn/apps/tbtx/miiee/css/base.css', function(){ console.log('css加載完畢'); }); loadScript('http://a.tbcdn.cn/apps/tbtx/miiee/js/jQuery.js', function(){ console.log('js加載完畢'); }); ready(function(){ console.log('dom is ready!'); });