每一個架構都有一個核心,所有的結構都是基於這個核心之上,結構建立好了之後,剩下的就是功能的堆砌。
jQuery的核心就是從HTML文檔中匹配元素並對其操作。
就跟一座大樓一樣,讓我們一步一步瞭解這座大廈的基石和結構。
1.建構函式
2.鏈式文法
3.選取器
4.擴充性
一、建構函式
我們知道類是物件導向編程的一個重要概念,它是對事物的最高抽象,它是一個模型。通過執行個體化一個類,我們可以建立一個執行個體。
javascript本身沒有類的概念,只有原型prototype,prototype是什麼呢?它是為建構函式設定的一個屬性,這個屬性包含一 個對象(簡稱prototype對象)。這個對象中包含了執行個體對象共用的屬性(properties)和方法(methods)。有了這麼一個屬性,我們 就可以讓所有的執行個體共用某些東西,來實現物件導向中類的概念。
下面用一個例子來解釋一下,連結--------------
因此jQuery利用原型繼承來實作類別的
1 var jQuery = function(){} 2 3 jQuery.prototype = { 4 //擴充的原型對象 5 } 6 //由於prototype名稱太長,我們可以另起一名fn 7 jQuery.fn = jQuery.prototype = { 8 //擴充的原型對象 9 }10 /********************我是分割線************************/11 //同理jQuery也可以用別名$來代替,因此代碼可以寫成12 var $ = jQuery = function(){};13 jQuery.fn = jQuery.prototype = {14 //擴充的原型對象15 jquery : "1.7.2",16 size : function(){17 return this.length;18 }19 }
那我們如何調用它嗎?
1 var my$ = new $();//執行個體化2 console.log(my$.jquery);//1.7.23 console.log(my$.size());//undefined
但是jQuery並不是這麼調用的,它類似$().jquery 這種形式。
也就是說jQuery沒用new執行個體化,而是直接調用jQuery()函數,然後後面跟jQuery的原型方法。怎麼實現呢?
1 var $ = jQuery = function(){ 2 return new jQuery(); 3 }; 4 jQuery.fn = jQuery.prototype = { 5 6 jquery : "1.7.2", 7 size : function(){ 8 return this.length; 9 }10 }11 12 console.log($().jquery);13 console.log($().size());
如果按照上面的做法會出錯,記憶體溢出,因為建立執行個體的時候循環參考導致出錯。
我們需要返回一個執行個體,我們知道在new一個對象的時候,this指向new的執行個體,執行個體擷取了prototype的屬性方法。
因此我們可以用Factory 方法建立一個執行個體,把這個方法放在jQuery.prototype 的原型對象當中,然後在jQuery函數中返回這個原型方法的調用。
1 var $ = jQuery = function(){ 2 return jQuery.fn.init(); 3 }; 4 jQuery.fn = jQuery.prototype = { 5 init: function(){ 6 console.log(this); 7 return this;//返回執行個體的引用 8 }, 9 jquery : "1.7.2",10 size : function(){11 return this.length;12 }13 }14 console.log($().jquery);15 console.log($().size());
console中會看到this對象是 jQuery的一個執行個體。
init()方法返回的是this關鍵字,該關鍵字引用的是jQuery的執行個體,如果在init()中繼續使用this關鍵字,也就是將init函數視為一個構造器,this又是如何處理呢?
var $ = jQuery = function(){ return jQuery.fn.init();}; jQuery.fn = jQuery.prototype = { init: function(){ this.length = 2; this.test = function(){ return this.length; } return this; }, jquery : "1.7.2", length:0, size : function(){ return this.length; }}console.log($().jquery);console.log($().test()); //2 ? 0 ?console.log($().size()); //2 ? 0 ?
返回的都是2,可以看到,this關鍵字引用了init函數範圍所在的對象,此時它訪問length屬性時,返回的為2。this關鍵字也能訪問上級對象jQuery.fn對象的範圍,所以返回1.7.2,而調用size方法時,返回的是2而不是0。
這種設計思路很容易破壞範圍的獨立性,對jQuery架構可能產生消極影響,因此jQuery通過執行個體化init初始化類型來分割範圍的
1 var $ = jQuery = function(){2 return new jQuery.fn.init();3 };
這樣就可以把init()構造函器中的this和jQuery.fn對象中的this關鍵字隔離開來。避免混淆。但是這種方法帶來的另一個問題是無法訪問jQuery.fn 的對象的屬性和方法。
Object [object Object] has no method 'size'.
如何做到既能分割初始化建構函式與jQuery原型對象的範圍,又能夠在返回執行個體中訪問jQuery原型對象呢?
jQuery架構巧妙地通過原型傳遞解決了這個問題
1 jQuery.fn.init.prototype = jQuery.fn;//使用jQuery原型對象覆蓋init原型對象
這樣 new jQuery.fn.init() 建立的新對象擁有init構造器的prototype原型對象的方法,通過改變prototype指標的指向,使其指向jQuery類的prototype,這樣創造出來的對象就繼承了jQuery.fn原型對象定義的方法。
二、擴充性
jQuery 自訂擴充方法用的extend () 函數
1 jQuery.extend = jQuery.fn.extend = function() {2 //code 3 }
在講源碼之前,先說一下什麼是拷貝,淺拷貝,深拷貝。
我們知道js 種不同的資料類型
* 基本類型:按值傳遞 (undefined,NULL,boolean,String,Number)
* 參考型別:傳遞記憶體位址 Object
/* 深度拷貝,所有的元素和屬性完全clone,並與原引用對象完全獨立,複製後的對象與原對象再也沒有任何關係,也就是當你拷貝完成後,原對象值有任何更改,都不會影響到我們複製後那個對象的值*/
所以我們在進行深拷貝(clone)的時候,注意將複製對象中的每一個值,而不是引用,換句話說,就是採用遞迴的方法淺拷貝對象。
1.淺拷貝
1 var clone = _.clone = function(obj){ 2 //不是對象直接放回返回值 3 if (typeof obj != 'object') return obj; 4 var result; 5 //數組用slice方法 不改變原數組 6 if(Object.prototype.toString.call(obj)==="[Object Array]"){ 7 result = obj.slice(); 8 }else{ 9 //對象 for遍曆10 result = {};11 for(var name in obj){12 result[name] = object[name];13 }14 }15 return result;16 }
2.深拷貝
1 var _deepClone = function(source){ 2 if(source===null) return null; 3 var result; 4 if(source instanceof Array){ 5 result = []; 6 //如果是數組,遞迴調用 7 for(var i = 0;i
3.jQuery 的實現
1 jQuery.extend = jQuery.fn.extend = function() { 2 //所有使用的的變數最好最好在函數定義的最前就寫下來,原因與ECMA有關 詳解 3 4 var options, name, src, copy, copyIsArray, clone, 5 target = arguments[0] || {}, 6 i = 1, 7 length = arguments.length, 8 deep = false; 9 10 // 判斷是否為深拷貝|淺拷貝11 if ( typeof target === "boolean" ) {12 deep = target;13 target = arguments[1] || {}; //返回的目標對象14 // 遍曆的時候跳過 deep | target 參數15 i = 2;16 }17 18 // 如果初始值不為對象 且不是一個函數則置空,比如一個string ""置為{};19 if ( typeof target !== "object" && !jQuery.isFunction(target) ) {20 target = {};21 }22 23 // 如果只有自己一個參數,而沒有被複製繼承的參數,則返回自己本身24 if ( length === i ) {25 target = this;26 --i;27 }28 29 for ( ; i < length; i++ ) {30 // 處理值為null undefined情況31 if ( (options = arguments[ i ]) != null ) {32 // 繼承對象options33 34 for ( name in options ) {35 src = target[ name ]; //原對象中對應的值36 copy = options[ name ]; //需要拷貝的對象中對應的值37 38 // 防止陷入死迴圈,如果原對象本身就等於需要拷貝的對象中的那值(o), //在對o遍曆的時候就把自己重新遍曆賦值了一遍39 if ( target === copy ) {40 continue;41 }42 43 //在 Array和Object的情況,且deep為true和傳進對象有值(true)的情況下,遞迴調用本身方法進行深拷貝44 if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {45 if ( copyIsArray ) {//如果需要拷貝的對象為數組46 copyIsArray = false;47 clone = src && jQuery.isArray(src) ? src : [];48 49 } else {50 clone = src && jQuery.isPlainObject(src) ? src : {};51 }52 53 target[ name ] = jQuery.extend( deep, clone, copy );54 // 如果不是上述情況,則進行淺拷貝,我們在執行jQuery.extend({})或jQuery.fn.extend({})的時候都是執行的這個方法,但卻搞不明白為什麼一個是對JQuery類的自訂擴充,一個是JQuery對象的自訂擴充,那麼這裡的target究竟代表什麼呢,我們看下面的例子55 } else if ( copy !== undefined ) {56 target[ name ] = copy;57 }58 }59 }60 }61 62 // 返回修改過後的target63 return target;64 };
注意:雖然jQuery.extend = jQuery.fn.extend 它們是一個方法,但是它們的具體作用是不一樣的,因為this的指向不同。
function jQuery() {}//使用字面量的方式建立原型對象,這裡{}就是對象,是Object,new Object就相當於{}jQuery.fn = jQuery.prototype = {constructor : jQuery,//強制指向jQueryname : 'Lee', age : 100,run : function () {return this.name + this.age + '運行中...';}};jQuery.extend = jQuery.fn.extend = function(){var option = arguments[0] ;for(var v in option){this[v] = option[v];}return this;};var jquery = new jQuery();document.write(""+jQuery.extend({add:function(){alert("aaaa");}})+"
");document.write(""+jQuery.fn.extend({minu:function(){alert("bbbb");}})+"
");jQuery.add();jquery.minu();this列印結果function jQuery() {}
[object Object]
在建構函式那個模組我們看到
jQuery .extend 的this 是jQuery類本身,在jQuery類上添加(對象,方法)
jQuery.fn.extend 的this是jQuery的原型,在jQuery原型上添加(對象,方法),那麼JQuery對象本身也會具有哪些方法
jQuery.fn = jQuery.prototype
四、來一個簡化版的
1 var _deepClone = function(source){ 2 if(source===null) return null; 3 var result; 4 if(source instanceof Array){ 5 result = []; 6 //如果是數組,遞迴調用 7 for(var i = 0;i