首页 > 代码库 > jQuery框架结构简析
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);
暂时先分析这些基本的功能实现过程。
学习是一个辛苦但又兴奋的过程,只有通过不断的努力,才能勉强不让自己在快速发展的节奏中脱节。
jQuery框架结构简析