一、 切入點:首先完成each方法和map方法的模擬;
each方法: jQuery的each方法,返回一個jQuery對象,即一個偽數組,因此each方法實現了鏈式編程。另外,jQuery的each方法的回調函數返回false,結束循環,內部的this指向當前遍歷的元素;
map方法: jQuery的map方法中,根據回調函數的放回結果來返回,如果回調函數返回的是數組,那么map方法返回數組,如果回調函數中沒有返回值,那么默認返回一個空數組。因此,map方法破壞了鏈式編程,內部的this不再指向遍歷元素,而是window;
二、框架搭建:
將整個框架的搭建放在一個沙箱中(閉包)
(funtion(window){})(window); // window 有兩個作用: 一是減少作用域的搜索,而是提高壓縮的效率
需要一個函數,在此用MHQ表示;
替換原型對象並還原構造器;
影藏new關鍵字: // MHQ並不通過new關鍵字,jQuery的做法是,在其原型對象上添加init構造函數,該構造函數的實例就是MHQ;
現在的問題是MHQ.prototype.init()構造函數的實例怎么調用MHQ原型對象上的方法? jQuery的做法是: 讓其原型對象上的init構造函數的原型對象指向其原型對象,也就是說:讓MHQ.prototype.init.prototype = MHQ.prototype;
那么現在如何要讓閉包中的MHQ暴漏給外部訪問呢? 答案是將其掛到全局window上,於是有了window.MHQ = window.M = MHQ; MHQ就是jQuery,那么M就是$,現在,window.MHQ就直接訪問MHQ原型上的方法了。
現在的問題是:如何給jQuery擴展方法呢? jQuery的做法是直接添加靜態屬性和實例方法,實現混入繼承。 MHQ.extend=MHQ.fn.extend=function(obj){for(var key in obj){this[k]=obj[k]}};將一個對象中的屬性和方法拷貝一份供自己使用;
另外jQuery中為了避免出現錯誤將所有的變量聲明提前。
1 (function(window){ 2 function MHQ(selector){ 3 return new MHQ.fn.init(selector); 4 } 5 MHQ.fn = MHQ.prototye = { 6 constructor: MHQ, 7 length:0, 8 init: function(selector){ 9 // 對傳入選擇器的一系列的判斷 10 } 11 }; 12 MHQ.fn.init.prototype = MHQ.fn; 13 MHQ.extend = MHQ.fn.extend = function(obj){ 14 var k; 15 for(k in obj){ 16 this[k] = obj[k]; 17 } 18 }; 19 // 擴展實例方法,通過MHQ.fn.init(selector)的實例來訪問 20 MHQ.fn.extend({...}); 21 // 擴展靜態方法,一般是工具類方法,如each,map 22 MHQ.extend({...}); 23 window.MHQ = window.M = MHQ; 24 })(window);
三、首先根據切入點一的分析封裝each和map方法
這里需要明確jQuery的$符里面能夠傳入的類型有:jQuery對象,字符串(html格式字符串和選擇器),DOM對象,函數等,因此需要判斷傳入的類型,因此也就有了其對應的判斷方式。
四、toArray方法和get方法的封裝
其中toArray方法沒有參數,get方法不傳參返回DOM元素的真數組,傳入參數為正數,返回對應下標的DOM元素,為負數從this.length開始計算返回DOM元素;
五、DOM操作的一些方法的封裝
在此需要注意的是鏈破壞,恢復鏈;
1 /** 2 * Created by mhq on 2016/10/26. 3 */ 4 // window 的兩個作用: 減少作用域的搜素,提高壓縮效率 5 (function (window) { 6 /*在閉包內部作用域中定義變量,提高效率*/ 7 var arr = []; 8 var push = arr.push; 9 var slice = arr.slice; 10 11 // MHQ的原型對象中的init構造函數創建對象,影藏了new關鍵字,返回一個偽數組 12 function MHQ(selector) { 13 return new MHQ.fn.init(selector); 14 } 15 // 替換原型對象的方式實現繼承 16 MHQ.fn = MHQ.prototype = { 17 // 還原構造器 18 constructor: MHQ, 19 // 添加length屬性,返回時是偽數組 20 length: 0, 21 init: function (selector) { 22 // 判斷選擇器類型 23 if (!selector) return this; 24 if (typeof selector === "string") { 25 // HTML格式的字符串 26 if (selector.charAt(0) === "<") { 27 push.apply(this, parseHTML(selector)); 28 return this; 29 } else { // 選擇器 30 push.apply(this, document.querySelectorAll(selector)); 31 return this; 32 } 33 } 34 if (typeof selector === "function") { 35 // 傳入類型是函數 將來處理事件 36 } 37 if (selector.nodeType) { // 是DOM對象時 38 this[0] = selector; 39 this.length = 1; 40 return this; 41 } 42 if (selector.constructor.name === MHQ) { // 是MHQ類型的對象 43 return selector; 44 } 45 if (selector.length >= 0) { // 是數組或者偽數組 46 push.apply(this, selector); 47 } else { 48 this[0] = selector; 49 this.length = 1; 50 } 51 } 52 }; 53 // 使init構造函數的實例能夠訪問MHQ原型對象中的方法 54 MHQ.fn.init.prototype = MHQ.fn; 55 // 混入實現繼承 56 MHQ.extend = MHQ.fn.extend = function (obj) { 57 var k; 58 for (k in obj) { 59 this[k] = obj[k]; 60 } 61 }; 62 63 MHQ.fn.extend({ 64 each: function (callback) { 65 return MHQ.each(this, callback); 66 }, 67 map: function (callback) { 68 return MHQ.map(this, callback); 69 } 70 }); 71 72 MHQ.extend({ 73 each: function (obj, callback) { 74 var i, 75 len = obj.length, 76 isArray = len >= 0; 77 if (isArray) { 78 for (i = 0; i < len; i++) { 79 if (callback.call(obj[i], i, obj[i]) === false) break; 80 } 81 } else { 82 for (i in obj) { 83 if (callback.call(obj[i], i, obj[i]) === false) break; 84 } 85 } 86 return obj; 87 }, 88 map: function (obj, callback) { 89 var i, 90 len = obj.length, 91 isArray = len >= 0, 92 result, 93 ret = []; 94 if (isArray) { 95 for (i = 0; i < len; i++) { 96 result = callback(obj[i], i); 97 if (result != null) { 98 ret.push(result); 99 } 100 } 101 } else { 102 for (i in obj) { 103 result = callback(obj[i], i); 104 if (result != null) { 105 ret.push(result); 106 } 107 } 108 } 109 return ret; 110 }, 111 next: function ( dom ) { 112 var node = dom; 113 while( node = node.nextSibling ) { 114 if ( node.nodeType === 1 ) { 115 return node; 116 } 117 } 118 return null; 119 } 120 }); 121 122 // 處理HTML字符串的方法 123 function parseHTML(htmlStr) { 124 var div = document.createElement("div"), 125 i = 0, 126 nodeArr = []; 127 div.innerHTML = htmlStr; 128 for (; i < div.childNodes.length; i++) { 129 nodeArr.push(div.childNodes[i]); 130 } 131 return nodeArr; 132 } 133 134 // 實現toArray方法、get方法(獲取DOM元素) 135 MHQ.fn.extend({ 136 toArray: function () { 137 return slice.call(this); 138 }, 139 get: function (index) { 140 if (index === undefined) { 141 return this.toArray(); 142 } else { 143 return this[index > 0 ? index : this.length + index]; 144 } 145 } 146 }); 147 148 // DOM元素操作模塊 149 MHQ.fn.extend({ 150 appendTo: function (selector) { 151 var i, 152 j, 153 tmpObj, 154 ret = [], 155 destinationObj = MHQ(selector); 156 157 for (i = 0; i < this.length; i++) { 158 for (j = 0; j < destinationObj.length; j++) { 159 tmpObj = j === destinationObj.length - 1 ? this[i] : this[i].cloneNode(true); 160 ret.push(tmpObj); 161 destinationObj[j].appendChild(tmpObj); 162 } 163 } 164 return this.pushStack(ret); 165 }, 166 prependTo: function (selector) { 167 // 將 this[i] 加入到 selector[j] 中, 鏈會破壞 168 // MHQ( selector ).prepend( this ); 169 var tmpObj, ret = [], 170 i, j, 171 destinationObj = MHQ(selector); 172 for (i = 0; i < this.length; i++) { 173 for (j = 0; j < destinationObj.length; j++) { 174 tmpObj = j === destinationObj.length - 1 ? this[i] : this[i].cloneNode(true); 175 ret.push(tmpObj); 176 destinationObj[j].insertBefore(tmpObj, destinationObj[j].firstChild); 177 } 178 } 179 180 return this.pushStack(ret); 181 }, 182 prepend: function (selector) { 183 MHQ(selector).appendTo(this); 184 return this; 185 }, 186 append: function (selector) { 187 // 將 selector[j] 加到 this[i] 中 188 // 不會造成鏈破壞 189 MHQ(selector).appendTo(this); 190 return this; 191 }, 192 next: function () { 193 /* 194 var ret = []; 195 this.each(function () { 196 ret.push( this.nextElementSibling ); 197 }); 198 return this.pushStack( ret ); 199 */ 200 return this.pushStack( 201 this.map(function ( v ) { 202 return MHQ.next( v ); 203 })); 204 }, 205 remove: function () { 206 this.each(function () { 207 this.parentNode.removeChild(this); 208 }); 209 } 210 }); 211 212 // 恢復鏈 213 MHQ.fn.extend({ 214 end: function () { 215 return this.prevObj || this; 216 }, 217 pushStack: function (array) { 218 var newObj = MHQ(array); 219 newObj.prevObj = this; 220 return newObj; 221 } 222 }); 223 224 window.MHQ = window.M = MHQ; 225 226 })(window);
暫時先分析這些基本的功能實現過程。
學習是一個辛苦但又興奮的過程,只有通過不斷的努力,才能勉強不讓自己在快速發展的節奏中脫節。