jquery构造器的实现代码小结


Posted in Javascript onMay 16, 2011

显然,能做到这一步,其实现是相当的复杂,这个实现就是它的init方法,jQuery的真实构造器。它功能也随着版本的升级而升级,越来越长。
2009-01-13发布的1.3版

init: function( selector, context ) { 
// Make sure that a selection was provided 
selector = selector || document; 
// 处理节点参数,直接添加属性到新实例上 
if ( selector.nodeType ) { 
this[0] = selector; 
this.length = 1; 
this.context = selector; 
return this; 
} 
// 处理字符串参数 
if ( typeof selector === "string" ) { 
// 判定是否为HTML片断还是ID 
var match = quickExpr.exec( selector ); 
if ( match && (match[1] || !context) ) { 
// 如果是HTML片断,转换一个由节点构造的数组 
if ( match[1] ) 
selector = jQuery.clean( [ match[1] ], context ); 
// 如果是ID,则查找此元素,如果找到放进空数组中 
else { 
var elem = document.getElementById( match[3] ); 
// Make sure an element was located 
if ( elem ){ 
// 处理 IE and Opera 混淆ID与NAME的bug 
if ( elem.id != match[3] ) 
return jQuery().find( selector ); 
var ret = jQuery( elem ); 
ret.context = document; 
ret.selector = selector; 
return ret; 
} 
selector = []; 
} 
} else 
//使用Sizzle处理其他CSS表达式,生成实例并返回 
return jQuery( context ).find( selector ); 
// 处理函数参数,直接domReady 
} else if ( jQuery.isFunction( selector ) ) 
return jQuery( document ).ready( selector ); 
//处理jQuery对象参数,简单地将其两个属性赋给新实例 
if ( selector.selector && selector.context ) { 
this.selector = selector.selector; 
this.context = selector.context; 
} 
//将上面得到节点数组,用setArray方法把它们变成实例的元素 
return this.setArray(jQuery.makeArray(selector)); 
},

2009-02-19发布的1.32版
init: function( selector, context ) { 
// Make sure that a selection was provided 
selector = selector || document; 
// 处理节点参数,直接添加属性到新实例上 
if ( selector.nodeType ) { 
this[0] = selector; 
this.length = 1; 
this.context = selector; 
return this; 
} 
//处理字符串参数 
if ( typeof selector === "string" ) { 
//判定是否为HTML片断还是ID 
var match = quickExpr.exec( selector ); 
if ( match && (match[1] || !context) ) { 
// 如果是HTML片断,转换一个由节点构造的数组 
if ( match[1] ) 
selector = jQuery.clean( [ match[1] ], context ); 
else { 
var elem = document.getElementById( match[3] ); 
// 如果是ID,则查找此元素,如果找到放进空数组中 
if ( elem && elem.id != match[3] ) 
return jQuery().find( selector ); 
//这里对1.3版做了些优化,更简洁 
var ret = jQuery( elem || [] ); 
ret.context = document; 
ret.selector = selector; 
return ret; 
} 
} else 
//使用Sizzle处理其他CSS表达式,生成实例并返回 
return jQuery( context ).find( selector ); 
// 处理函数参数,进行domReady操作 
} else if ( jQuery.isFunction( selector ) ) 
return jQuery( document ).ready( selector ); 
//处理jQuery对象参数,简单地将其两个属性赋给新实例 
if ( selector.selector && selector.context ) { 
this.selector = selector.selector; 
this.context = selector.context; 
} 
//这里对1.3版做了些扩展,允许传珍上元素集合(HTMLCollection)与节点集合(NodeList), 
//元素数组可能是我们用字符串转换过来的,也可以是用户直接传进来的 
return this.setArray(jQuery.isArray( selector ) ? selector : jQuery.makeArray(selector)); 
},

2010-01-13发布的1.4版
init: function( selector, context ) { 
var match, elem, ret, doc; 
//处理空白字符串,null,undefined参数(新增),返回一个非常纯净的实例 
if ( !selector ) { 
return this; 
} 
// 处理节点参数,直接添加属性到新实例上 
if ( selector.nodeType ) { 
this.context = this[0] = selector;//写法上优化 
this.length = 1; 
return this; 
} 
//处理字符串参数 
if ( typeof selector === "string" ) { 
// 判定是否为HTML片断还是ID 
match = quickExpr.exec( selector ); 
if ( match && (match[1] || !context) ) { 
//如果是HTML片断 
if ( match[1] ) { 
//取得文档对象 
doc = (context ? context.ownerDocument || context : document); 
// 如果是单个标签,直接使用 document.createElement创建此节点并放入数组中 
ret = rsingleTag.exec( selector ); 
if ( ret ) { 
//如果后面跟着一个纯净的JS对象,则为此节点添加相应的属性或样式 
if ( jQuery.isPlainObject( context ) ) { 
selector = [ document.createElement( ret[1] ) ]; 
jQuery.fn.attr.call( selector, context, true ); 
} else { 
selector = [ doc.createElement( ret[1] ) ]; 
} 
} else { 
//改由buildFragment来生成节点集合(NodeList) 
ret = buildFragment( [ match[1] ], [ doc ] ); 
selector = (ret.cacheable ? ret.fragment.cloneNode(true) : ret.fragment).childNodes; 
} 
} else { 
// 如果是ID,则查找此元素,如果找到放进空数组中 
elem = document.getElementById( match[2] ); 
if ( elem ) { 
// 处理 IE and Opera 混淆ID与NAME的bug 
if ( elem.id !== match[2] ) { 
return rootjQuery.find( selector ); 
} 
//这里也做了一些优化,原来是很傻地再生成一个jQuery实例 
this.length = 1; 
this[0] = elem; 
} 
this.context = document; 
this.selector = selector; 
return this; 
} 
// 如果字符是很简单的标签选择器,那基本没有必要走Sizzle路线,直接getElementsByTagName,很好的优化 
} else if ( !context && /^\w+$/.test( selector ) ) { 
this.selector = selector; 
this.context = document; 
selector = document.getElementsByTagName( selector ); 
// 如果第二个参数不存在或者是jQuery对象,那么用它或rootjQuery调用find查找目标节点(走Sizzle路线) 
} else if ( !context || context.jquery ) { 
return (context || rootjQuery).find( selector ); 
// HANDLE: $(expr, context) 
// (which is just equivalent to: $(context).find(expr) 
} else { 
//如果第二个参数已指定为某元素节点,转为jQuery对象,走Sizzle路线 
return jQuery( context ).find( selector ); 
} 
// 处理函数参数,直接domReady 
} else if ( jQuery.isFunction( selector ) ) { 
return rootjQuery.ready( selector ); 
} 
//处理jQuery对象参数,简单地将其两个属性赋给新实例 
if (selector.selector !== undefined) { 
this.selector = selector.selector; 
this.context = selector.context; 
} 
//这里又做了些许修改,缘于makeArray可以接受第二个参数(可以是数组或类数组,这时相当合并操作) 
return jQuery.isArray( selector ) ? 
this.setArray( selector ) ://内部用push方法,迅速将一个普通对象变成类数组对象 
jQuery.makeArray( selector, this ); 
},

接着是广受欢迎的2010-02-13发布的1.42版
init: function( selector, context ) { 
var match, elem, ret, doc; 
// 处理空白字符串,null,undefined参数 
if ( !selector ) { 
return this; 
} 
// 处理节点参数 
if ( selector.nodeType ) { 
this.context = this[0] = selector; 
this.length = 1; 
return this; 
} 
// 处理body参数(新增) 
if ( selector === "body" && !context ) { 
this.context = document; 
this[0] = document.body; 
this.selector = "body"; 
this.length = 1; 
return this; 
} 
// 处理字符串参数,分七种情形: 
//①单个标签,带对象属性包 ---> jQuery.merge 
//②单个标签,不带对象属性包 ---> attr + jQuery.merge 
//③复杂的HTML片断 ---> buildFragment + jQuery.merge 
//④ID选择器,与找到的元素的ID不同 ---> getElementById + Sizzle + pushStack 
//⑤ID选择器,与找到的元素的ID相同 ---> getElementById + 简单属性添加 
//⑥标签选择器 ---> getElementsByTagName + jQuery.merge 
//⑦其他CSS表达式 ---> Sizzle + pushStack 
if ( typeof selector === "string" ) { 
match = quickExpr.exec( selector ); 
if ( match && (match[1] || !context) ) { 
if ( match[1] ) { 
doc = (context ? context.ownerDocument || context : document); 
ret = rsingleTag.exec( selector ); 
if ( ret ) { 
if ( jQuery.isPlainObject( context ) ) { 
selector = [ document.createElement( ret[1] ) ]; 
jQuery.fn.attr.call( selector, context, true ); 
} else { 
selector = [ doc.createElement( ret[1] ) ]; 
} 
} else { 
ret = buildFragment( [ match[1] ], [ doc ] ); 
selector = (ret.cacheable ? ret.fragment.cloneNode(true) : ret.fragment).childNodes; 
} 
return jQuery.merge( this, selector ); 
} else { 
elem = document.getElementById( match[2] ); 
if ( elem ) { 
if ( elem.id !== match[2] ) { 
return rootjQuery.find( selector ); 
} 
this.length = 1; 
this[0] = elem; 
} 
this.context = document; 
this.selector = selector; 
return this; 
} 
} else if ( !context && /^\w+$/.test( selector ) ) { 
this.selector = selector; 
this.context = document; 
selector = document.getElementsByTagName( selector ); 
return jQuery.merge( this, selector ); 
} else if ( !context || context.jquery ) { 
return (context || rootjQuery).find( selector ); 
} else { 
return jQuery( context ).find( selector ); 
} 
// 处理函数参数,直接domReady 
} else if ( jQuery.isFunction( selector ) ) { 
return rootjQuery.ready( selector ); 
} 
//处理jQuery对象参数 
if (selector.selector !== undefined) { 
this.selector = selector.selector; 
this.context = selector.context; 
} 
//无论是数组还是类数组(如NodeList),统统使用jQuery.makeArray来为实例添加新的元素 
return jQuery.makeArray( selector, this ); 
},

另附上makeArray方法与merge方法,merge方法好神奇啊,
makeArray: function( array, results ) { 
var ret = results || []; 
if ( array != null ) { 
// The window, strings (and functions) also have 'length' 
// The extra typeof function check is to prevent crashes 
// in Safari 2 (See: #3039) 
if ( array.length == null || typeof array === "string" || jQuery.isFunction(array) || (typeof array !== "function" && array.setInterval) ) { 
push.call( ret, array ); 
} else { 
jQuery.merge( ret, array ); 
} 
} 
return ret; 
}, 
merge: function( first, second ) { 
var i = first.length, j = 0; 
if ( typeof second.length === "number" ) { 
for ( var l = second.length; j < l; j++ ) { 
first[ i++ ] = second[ j ]; 
} 
} else { 
while ( second[j] !== undefined ) { 
first[ i++ ] = second[ j++ ]; 
} 
} 
first.length = i; 
return first; 
},

2011-01-23发布的1.5版,其init方法与1.42的变化不大:只有两处做了改动:
//1.42 
- ret = buildFragment( [ match[1] ], [ doc ] ); 
- selector = (ret.cacheable ? ret.fragment.cloneNode(true) : ret.fragment).childNodes; 
//1.5 
+ ret = jQuery.buildFragment( [ match[1] ], [ doc ] ); 
+ selector = (ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment).childNodes; 
//1.42 
- return jQuery( context ).find( selector ); 
//1.5 
+ return this.constructor( context ).find( selector );//目的就是为了不再生成新实例

2011-05-02发布的jquery1.6,变化不大,只是对HTML片断进行了更严密的判定:
// Are we dealing with HTML string or an ID? 
if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) { 
// Assume that strings that start and end with <> are HTML and skip the regex check 
match = [ null, selector, null ]; 
} else { 
match = quickExpr.exec( selector ); 
}

总体来说,jQuery的构造器已经做得非常之完美,基本上达到“改无可改”的地步了。但是要保证其高效运作,我们还需要一点选择器的知识与了解buildFragment方法的运作,因为这两个实在太常用了,但也是最耗性能的。
Javascript 相关文章推荐
javascript 面向对象编程  function是方法(函数)
Sep 17 Javascript
ASP.NET jQuery 实例16 通过控件CustomValidator验证RadioButtonList
Feb 03 Javascript
使用javascript实现简单的选项卡切换
Jan 09 Javascript
javascript解决IE6下hover问题的方法
Jul 28 Javascript
JS+CSS实现自动切换的网页滑动门菜单效果代码
Sep 14 Javascript
使用纯JS代码判断字符串中有多少汉字的实现方法(超简单实用)
Nov 12 Javascript
Jquery Easyui进度条组件Progress使用详解(8)
Mar 26 Javascript
JavaScript页面倒计时功能完整示例
May 15 Javascript
微信小程序实现侧边栏分类
Oct 21 Javascript
jQuery 移除事件的方法
Jun 20 jQuery
javascript实现搜索筛选功能实例代码
Nov 12 Javascript
原生js实现无缝轮播图效果
Jan 28 Javascript
让innerText在firefox火狐和IE浏览器都能用的写法
May 14 #Javascript
模仿百度三维地图的js数据分享
May 12 #Javascript
javascript数组去掉重复
May 12 #Javascript
javascript 弹出层组件(升级版)
May 12 #Javascript
ExtJS4 组件化编程,动态加载,面向对象,Direct
May 12 #Javascript
关于js获取radio和select的属性并控制的代码
May 12 #Javascript
js 第二代身份证号码的验证机制代码
May 12 #Javascript
You might like
php 什么是PEAR?
2009/03/19 PHP
php数组的一些常见操作汇总
2011/07/17 PHP
php中的mongodb select常用操作代码示例
2014/09/06 PHP
php禁止浏览器使用缓存页面的方法
2014/11/07 PHP
php银联网页支付实现方法
2015/03/04 PHP
类似GMAIL的Ajax信息反馈显示
2010/02/16 Javascript
js防止表单重复提交实现代码
2012/09/05 Javascript
jquery 利用show和hidden实现级联菜单示例代码
2013/08/09 Javascript
jquery的map与get方法详解
2013/11/04 Javascript
查看大图功能代码jquery版
2013/11/05 Javascript
JS实现很酷的水波文字特效实例
2015/02/26 Javascript
jquery 重写 ajax提交并判断权限后 使用load方法报错解决方法
2016/01/19 Javascript
jQuery Ajax 上传文件处理方式介绍(推荐)
2016/06/30 Javascript
require、backbone等重构手机图片查看器
2016/11/17 Javascript
AngularJS入门教程二:在路由中传递参数的方法分析
2017/05/27 Javascript
微信小程序出现wx.navigateTo页面不跳转问题的解决方法
2017/12/26 Javascript
nodejs 使用 js 模块的方法实例详解
2018/12/04 NodeJs
手写Vue弹窗Modal的实现代码
2019/09/11 Javascript
简述Vue中容易被忽视的知识点
2019/12/09 Javascript
使用python BeautifulSoup库抓取58手机维修信息
2013/11/21 Python
python批量同步web服务器代码核心程序
2014/09/01 Python
django+js+ajax实现刷新页面的方法
2017/05/22 Python
Python2.7基于淘宝接口获取IP地址所在地理位置的方法【测试可用】
2017/06/07 Python
itchat和matplotlib的结合使用爬取微信信息的实例
2017/08/25 Python
python执行系统命令后获取返回值的几种方式集合
2018/05/12 Python
谈谈Python中的while循环语句
2019/03/10 Python
.dcm格式文件软件读取及python处理详解
2020/01/16 Python
python实现提取COCO,VOC数据集中特定的类
2020/03/10 Python
十八届三中全会宣传方案
2014/02/21 职场文书
欢度春节标语
2014/07/01 职场文书
我们的节日国庆活动方案
2014/08/19 职场文书
护士优质服务演讲稿
2014/08/26 职场文书
2014教师年度思想工作总结
2014/11/10 职场文书
停水通知
2015/04/16 职场文书
2019年大学毕业生个人自我鉴定范文大全
2019/03/21 职场文书
2019最新版试用期劳动合同模板!
2019/07/04 职场文书