jQuery框架結構簡析


  一、 切入點:首先完成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);

  暫時先分析這些基本的功能實現過程。

  

  學習是一個辛苦但又興奮的過程,只有通過不斷的努力,才能勉強不讓自己在快速發展的節奏中脫節。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM