上一篇已經搭建了一個很是簡陋的jQuery框架雛形,如沒有閱讀搭建過程,請先閱讀《jQuery內核詳解與實踐讀書筆記1:原型技術分解1》初始搭建過程。接下來,完成書中介紹的剩下三個步驟:html
7. 延續--功能擴展node
jQuery框架是經過extend()函數來擴展功能的,extend()函數的功能實現起來也很簡單,它只是吧指定對象的方法複製給jQuery對象或jQuery.prototype對象,以下示例代碼就爲jQuery類和原型定義了一個擴展功能的函數extend()。jquery
1 var $ = jQuery = function(selector, context) { //定義類 2 return new jQuery.fn.init(selector, context); //返回選擇器的實例 3 }; 4 jQuery.fn = jQuery.prototype = { //jQuery類的原型對象 5 init : function(selector, context) { //定義選擇器構造器 6 selector = selector || document; //設置默認值爲document 7 context = context || document; //設置默認值爲document 8 if(selector.nodeType) { //若是選擇符爲節點對象 9 this[0] = selector; //把參數節點傳遞給實例對象的數組 10 this.length = 1; //並設置實例對象的length屬性,定義包含的元素個數 11 this.context = selector; //設置實例的屬性,返回選擇範圍 12 return this; //返回當前實例 13 } 14 if(typeof selector === "string") { //若是選擇符是字符串 15 var e = context.getElementsByTagName(selector); //獲取指定名稱的元素 16 for(var i=0; i<e.length; i++) { //遍歷元素集合,並把全部元素填入到當前實例數組中 17 this[i] = e[i]; 18 } 19 this.length = e.length; //設置實例的length屬性,即定義包含的元素個數 20 this.context = context; //設置實例的屬性,返回選擇範圍 21 return this; //返回當前實例 22 } else { 23 this.length = 0; //不然,設置實例的length屬性值爲0 24 this.context = context; //設置實例的屬性,返回選擇範圍 25 return this; //返回當前實例 26 } 27 }, 28 jquery : "1.3.2", //原型屬性 29 size : function() { //原型方法 30 return this.length; 31 } 32 }; 33 jQuery.fn.init.prototype = jQuery.fn; //使用jQuery的原型對象覆蓋init的原型對象 34 //jQuery功能擴展函數 35 jQuery.extend = jQuery.fn.extend = function(obj) { 36 for(var prop in obj) { 37 this[prop] = obj[prop]; 38 } 39 return this; 40 }; 41 //擴展jQuery對象方法 42 jQuery.fn.extend({ 43 test : function() { 44 alert("測試擴展功能"); 45 } 46 });
在上面的代碼中,先定義了一個功能擴展函數extend(),而後爲jQuery.fn原型對象調用extend()函數,爲其添加一個測試方法test()。這樣就能夠在實踐中應用,如$("div").test()。數組
jQuery框架定義的extend()函數的功能要強大不少,它不只可以完成基本的功能擴展,還能夠實現對象合併等功能,代碼和解釋以下所示:緩存
1 var $ = jQuery = function(selector, context) { //定義類 2 return new jQuery.fn.init(selector, context); //返回選擇器的實例 3 }; 4 jQuery.fn = jQuery.prototype = { //jQuery類的原型對象 5 init : function(selector, context) { //定義選擇器構造器 6 selector = selector || document; //設置默認值爲document 7 context = context || document; //設置默認值爲document 8 if(selector.nodeType) { //若是選擇符爲節點對象 9 this[0] = selector; //把參數節點傳遞給實例對象的數組 10 this.length = 1; //並設置實例對象的length屬性,定義包含的元素個數 11 this.context = selector; //設置實例的屬性,返回選擇範圍 12 return this; //返回當前實例 13 } 14 if(typeof selector === "string") { //若是選擇符是字符串 15 var e = context.getElementsByTagName(selector); //獲取指定名稱的元素 16 for(var i=0; i<e.length; i++) { //遍歷元素集合,並把全部元素填入到當前實例數組中 17 this[i] = e[i]; 18 } 19 this.length = e.length; //設置實例的length屬性,即定義包含的元素個數 20 this.context = context; //設置實例的屬性,返回選擇範圍 21 return this; //返回當前實例 22 } else { 23 this.length = 0; //不然,設置實例的length屬性值爲0 24 this.context = context; //設置實例的屬性,返回選擇範圍 25 return this; //返回當前實例 26 } 27 }, 28 jquery : "1.3.2", //原型屬性 29 size : function() { //原型方法 30 return this.length; 31 } 32 }; 33 jQuery.fn.init.prototype = jQuery.fn; //使用jQuery的原型對象覆蓋init的原型對象 34 //jQuery功能擴展函數 35 jQuery.extend = jQuery.fn.extend = function(obj) { 36 //定義複製操做的目標對象 37 var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options; 38 //獲取是否深度複製處理 39 if( typeof target === "boolean") { 40 deep = target; 41 target = arguments[i] || {}; 42 //跳出布爾值和目標對象 43 i = 2; 44 } 45 //若是第一個參數是字符串,則設置爲空對象 46 if(typeof target !== "object" && !jQuery.isFunction(target)) { 47 target = {}; 48 } 49 //若是隻有一個參數,表示把參數對象的方法複製給當前對象,則設置target爲this 50 if(length == i) { 51 target = this; 52 --i; 53 } 54 for( ; i<length; i++) { 55 //若參數值不爲null,則進行處理 56 if((options = arguments[i]) != null) { 57 //擴展基類對象 58 for( var name in options) { //遍歷參數對象 59 var src = target[name], copy = options[name]; 60 //防止死循環 61 if(target === copy) { 62 continue; 63 } 64 //遞歸運算 65 if(deep && copy && typeof copy === "object" && !copy.nodeType) { 66 target[name] = jQuery.extend(deep, 67 //不要複製原對象 68 src || (copy.length != null ? [] : {}), copy); 69 } else if(copy != undefined) { //不要傳遞未定義的值 70 target[name] = copy; 71 } 72 } 73 } 74 } 75 //返回修改後的對象 76 return target; 77 };
以上代碼就是jQuery框架提供的功能擴展代碼。微信
8. 延續--參數處理閉包
jQuery的方法都要求傳遞的參數爲對象結構,這是由於jQuery框架的不少方法都包含大量的參數,且都是可選的,位置也沒有固定的要求,因此使用對象直接量是惟一的解決方法。
使用對象直接量做爲參數傳遞的載體,如何解析並提出參數?如何處理參數的默認值?能夠經過下面的方式:架構
1 var $ = jQuery = function(selector, context) { //定義類 2 return new jQuery.fn.init(selector, context); //返回選擇器的實例 3 }; 4 jQuery.fn = jQuery.prototype = { //jQuery類的原型對象 5 init : function(selector, context) { //定義選擇器構造器 6 selector = selector || document; //設置默認值爲document 7 context = context || document; //設置默認值爲document 8 if(selector.nodeType) { //若是選擇符爲節點對象 9 this[0] = selector; //把參數節點傳遞給實例對象的數組 10 this.length = 1; //並設置實例對象的length屬性,定義包含的元素個數 11 this.context = selector; //設置實例的屬性,返回選擇範圍 12 return this; //返回當前實例 13 } 14 if(typeof selector === "string") { //若是選擇符是字符串 15 var e = context.getElementsByTagName(selector); //獲取指定名稱的元素 16 for(var i=0; i<e.length; i++) { //遍歷元素集合,並把全部元素填入到當前實例數組中 17 this[i] = e[i]; 18 } 19 this.length = e.length; //設置實例的length屬性,即定義包含的元素個數 20 this.context = context; //設置實例的屬性,返回選擇範圍 21 return this; //返回當前實例 22 } else { 23 this.length = 0; //不然,設置實例的length屬性值爲0 24 this.context = context; //設置實例的屬性,返回選擇範圍 25 return this; //返回當前實例 26 } 27 }, 28 setOptions : function(options) { 29 this.options = { //方法的默認值,能夠擴展 30 StartColor : "#000", 31 EndColor : "#DDC", 32 Background : false, 33 Step : 20, 34 Speed : 10 35 }; 36 jQuery.extend(this.options, options || {}); //若是傳遞參數,則覆蓋原默認參數 37 } 38 }; 39 jQuery.fn.init.prototype = jQuery.fn; //使用jQuery的原型對象覆蓋init的原型對象 40 //jQuery功能擴展函數 41 jQuery.extend = jQuery.fn.extend = function(destination, source) {//從新定義extend()函數 42 for(var property in source) { 43 destination[property] = source[proprty]; 44 } 45 return destination; 46 };
在上面的示例中,定義了一個原型方法setOptions(),該方法可以對傳遞的參數對象進行處理,並覆蓋默認值。
在jQuery框架中,extend()函數包含了全部功能,它既能爲當前對象擴展方法,也能處理參數對象,並覆蓋默認值。框架
9. 涅槃--名字空間函數
在介紹該內容前,先介紹一個JavaScript函數中的一個核心概念:閉包,其實就是匿名函數。閉包的一個很重要的做用就是將閉包內部的代碼封裝在一個封閉的空間中,不將內部的信息暴露出來,別的代碼也不能隨意訪問閉包內的函數或變量等。只須要提供外接能夠訪問的接口,就能夠方便的與外接進行聯繫。
咱們看jQuery源代碼時就會發現,它幾千行的代碼就是封裝在一個閉包之中的,這樣就解決了框架內部的變量名與其它框架或JavaScript代碼重名衝突問題。另外jQuery框架的$和jQuery名字也頗有可能發生名字衝突,這個問題jQuery框架是如何解決的呢?
首先,jQuery的全部代碼所有封裝在一個閉包(即匿名函數)中
其次,jQuery提供了一個noConfilit()函數,該函數實現禁止jQuery框架使用這兩個名字。
那它又是如何作的呢?
jQuery在框架的最前面,先使用_$和_jQuery臨時變量寄存$和jQuery這兩個變量的內容,當須要禁用jQuery框架的名字時,可使用一個臨時變量_$和_jQuery恢復$和jQuery這兩個變量的實際內容。代碼以下:
1 (function(){ 2 var window = this, 3 undefined, 4 _jQuery = window.jQuery, //緩存jQuery變量內容 5 _$ = window.$, 6 jQuery = window.jQuery = window.$ = function(selector, context) { 7 return new jQuery.fn.init(selector, context); 8 }, 9 quickExpr = /^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/, 10 isSimple = /^.[^:#\[\.,]*$/; 11 jQuery.fn = jQuery.prototype = { 12 init : function(selector, context) {} 13 }; 14 })();
至此,jQuery框架的簡單雛形就已經搭好了,雖然jQuery框架的功能是極其強大的,可是它也是在這個架構上創建起來的。後面的工做就是根據應用須要或者功能須要,使用extend()函數不斷擴展jQuery框架的工具函數和jQuery對象的方法。
今天就先寫到這裏了,下次就要開始研究jQuery的選擇器接口了,歡迎轉載,轉載時請註明出處。
我的微信公衆號:programmlife,若有興趣敬請關注,主要內容是一個碼農的所看所思所想所嘆,或掃描下方二維碼關注: