作者:zuoxiaolong8810(左瀟龍),轉載請注明出處,特別說明:本博文來自博主原博客,為保證新博客中博文的完整性,特復制到此留存,如需轉載請注明新博客地址即可。
前段時間上班無聊之時,研究了下jquery的源碼。現在記錄下自己的成果,分享一下。
下面是我自己琢磨和編寫的jquery模型,里面有我所寫的注釋。
/* * my-jquery-1.0 */ /* * 網上也有很多實現的版本,不過這是我在我自己的理解下寫的,加上注釋,希望可以解釋清楚。 */ /* * 整個jquery包含在一個匿名函數中,專業點叫閉包,就是下面的形式,如(function(window,undefined){}(window))。 * 閉包的定義在這里不太容易講清楚,我只說下這樣說的好處。 * 1.使整個jquery中定義的變量成為局域變量,不會影響全局變量,個人覺得這也是jquery被成為輕量級的原因之一。 * 2.增加jquery運行速度,因為局域變量運行速度高於全局變量。 * 3.就像你看到,傳入的window和undefined,可以自定義名字,方便編寫。當然,現在你看到的仍是原來的寫法,但是你可以看看jquery的min版本,一定是壓縮的。 */ (function( window, undefined ) { var /*jquery的定義,我們平時用的$和jQuery就是它。這里可以看出來真正的jQuery的對象是init方法產生的。 *這樣做采用了工廠模式,創建jQuery對象時不需要再new一個對象了。所以你可以發現,我們創建jQuery對象的方式是$(selector)或者是jQuery(selector) *原版的jQuery定義方法多了個上下文參數context,此處我省略了。 */ jQuery = function(selector){ return new jQuery.fn.init(selector); }, /* * 引用數據、對象以及字符串的方法 */ core_push = Array.prototype.push, core_slice = Array.prototype.slice, core_indexOf = Array.prototype.indexOf, core_toString = Object.prototype.toString, core_hasOwn = Object.prototype.hasOwnProperty, core_trim = String.prototype.trim; /* * jQuery對象的定義,這里去掉了所有的屬性,只留下了init()。 * jQuery的選擇器采用了Sizzle,這里省略了,可以看出我只簡單的返回了一個查詢ID的方式。 * jQuery的對象並不是這樣簡單的賦給了對象的一個屬性,而是創建了一個數組。在這里忽略那些,只是賦給了obj屬性。 * 這里jQuery將原型賦給了jQuery的fn屬性,所以我們如果要給jQuery對象擴展,只需要對jQuery.fn擴展就行。 */ jQuery.fn = jQuery.prototype = { init:function(selector){ this.obj = window.document.getElementById(selector); return this; } }; /* * 將jQuery的原型賦給init,這樣是為了可以讓jQuery對象,也就是init對象可以使用jQuery的擴展方法。 */ jQuery.fn.init.prototype = jQuery.fn; /* * jQuery的擴展方法,這個是jQuery的原版方法,我沒做更改。 * 方法的邏輯在這里不再說明,方法的效果就是,我們使用jQuery.extend可以擴展jQuery,而jQuery.fn.extend可以擴展jQuery對象。 */ jQuery.extend = jQuery.fn.extend = function() { var options, name, src, copy, copyIsArray, clone, target = arguments[0] || {}, i = 1, length = arguments.length, deep = false; // Handle a deep copy situation if ( typeof target === "boolean" ) { deep = target; target = arguments[1] || {}; // skip the boolean and the target i = 2; } // Handle case when target is a string or something (possible in deep copy) if ( typeof target !== "object" && !d.isFunction(target) ) { target = {}; } // extend jQuery itself if only one argument is passed if ( length === i ) { target = this; --i; } for ( ; i < length; i++ ) { // Only deal with non-null/undefined values if ( (options = arguments[ i ]) != null ) { // Extend the base object for ( name in options ) { src = target[ name ]; copy = options[ name ]; // Prevent never-ending loop if ( target === copy ) { continue; } // Recurse if we're merging plain objects or arrays if ( deep && copy && ( d.isPlainObject(copy) || (copyIsArray = d.isArray(copy)) ) ) { if ( copyIsArray ) { copyIsArray = false; clone = src && d.isArray(src) ? src : []; } else { clone = src && d.isPlainObject(src) ? src : {}; } // Never move original objects, clone them target[ name ] = d.extend( deep, clone, copy ); // Don't bring in undefined values } else if ( copy !== undefined ) { target[ name ] = copy; } } } } // Return the modified object return target; }; /* * 這里實現了簡單的ready綁定序列。 */ jQuery.extend({ isReady:false,//文檔加載是否完成的標識 readyList:[],//函數序列 //以下為工具方法,可忽略 isArray : Array.isArray || function( obj ) { return jQuery.type(obj) === "array"; }, isWindow : function( obj ) { return obj != null && obj == obj.window; }, isNumeric : function( obj ) { return !isNaN( parseFloat(obj) ) && isFinite( obj ); }, type : function( obj ) { return obj == null ? String( obj ) : class2type[ core_toString.call(obj) ] || "object"; }, isPlainObject : function( obj ) { // Must be an Object. // Because of IE, we also have to check the presence of the constructor property. // Make sure that DOM nodes and window objects don't pass through, as well if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { return false; } try { // Not own constructor property must be Object if ( obj.constructor && !core_hasOwn.call(obj, "constructor") && !core_hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { return false; } } catch ( e ) { // IE8,9 Will throw exceptions on certain host objects #9897 return false; } // Own properties are enumerated firstly, so to speed up, // if last one is own, then all properties are own. var key; for ( key in obj ) {} return key === undefined || core_hasOwn.call( obj, key ); }, isFunction : function(obj){ if(obj && typeof obj == 'function'){ return true; } return false; }, //onload事件實現 ready : function(fn){ //如果是函數,加入到函數序列 if(fn && typeof fn == 'function' ){ jQuery.readyList.push(fn); } //文檔加載完成,執行函數序列。 if(jQuery.isReady){ for(var i = 0;i < jQuery.readyList.length ;i++){ fn = jQuery.readyList[i]; jQuery.callback(fn); } return jQuery; } }, //回調 callback : function(fn){ fn.call(document,jQuery); } }); //模擬實現jQuery的html方法 jQuery.fn.extend({ html : function(html){ if(html && typeof html == 'string'){ this.obj.innerHTML = html; return this; } return this.obj.innerHTML; } }); //導出對象 window.$ = window.jQuery = jQuery; //判斷加載是否完成 var top = false; try { top = window.frameElement == null && document.documentElement; } catch(e) {} if ( top && top.doScroll ) { (function doScrollCheck() { try { top.doScroll("left"); jQuery.isReady = true; jQuery.ready(); } catch(e) { setTimeout( doScrollCheck, 50 ); } })(); } }(window));
下面這個是測試文件。
<html> <head> <script src="my-jquery-1.0.js" type="text/javascript"></script> <script type="text/javascript"> $.extend({ testExtend:function(){ alert('extend success'); } }); $.ready(function(){ alert($("test").html()); $.testExtend(); }); $.ready(function(){ $("test").html("modify success"); }); $.ready(function(){ alert($("test").html()); }); </script> </head> <body> <div id="test">jquery src</div> </body> </html>
兩個文件復制出來放在同一個文件夾下面,就可以看到效果。希望各位有不同意見的可以提出來一起探討。