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 相关文章推荐
jquery下json数组的操作实现代码
Aug 09 Javascript
浅谈jQuery中 wrap() wrapAll() 与 wrapInner()的差异
Nov 12 Javascript
DOM基础教程之使用DOM控制表格
Jan 20 Javascript
jquery trigger函数执行两次的解决方法
Feb 29 Javascript
基于jQuery实现照片墙自动播放特效
Jan 12 Javascript
jQuery插件FusionCharts实现的MSBar3D图效果示例【附demo源码】
Mar 23 jQuery
js实现股票实时刷新数据案例
May 14 Javascript
浅谈Angular路由守卫
Aug 26 Javascript
解决在Bootstrap模糊框中使用WebUploader的问题
Mar 22 Javascript
jQuery常见的遍历DOM操作详解
Sep 05 jQuery
jQuery基于随机数解决中午吃什么去哪吃问题示例
Dec 29 jQuery
vue.js+elementUI实现点击左右箭头切换头像功能(类似轮播图效果)
Sep 05 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文章采集URL补全函数(FormatUrl)
2012/08/02 PHP
phpadmin如何导入导出大数据文件及php.ini参数修改
2013/02/18 PHP
php设计模式之中介者模式分析【星际争霸游戏案例】
2020/03/23 PHP
js 编写规范
2010/03/03 Javascript
原生javascript图片自动或手动切换示例附演示源码
2013/09/04 Javascript
js将控件隐藏及display属性的使用介绍
2013/12/30 Javascript
js正则表达式中exec用法实例
2015/07/23 Javascript
JavaScript iframe数据共享接口实现方法
2016/01/06 Javascript
js组件SlotMachine实现图片切换效果制作抽奖系统
2016/04/17 Javascript
JavaScript 数组中最大最小值
2016/06/05 Javascript
AngularJs Modules详解及示例代码
2016/09/01 Javascript
微信小程序 新建登录页并实现tabBar隐藏
2017/06/13 Javascript
用node撸一个监测复联4开售短信提醒的实现代码
2019/04/10 Javascript
vue实现新闻展示页的步骤详解
2019/04/11 Javascript
vue中多路由表头吸顶实现的几种布局方式
2019/04/12 Javascript
elementUI table表格动态合并的示例代码
2019/05/15 Javascript
Python、Javascript中的闭包比较
2015/02/04 Python
Linux 下 Python 实现按任意键退出的实现方法
2016/09/25 Python
Django中url的反向查询的方法
2018/03/14 Python
python3.4实现邮件发送功能
2018/05/28 Python
对python模块中多个类的用法详解
2019/01/10 Python
python 去除二维数组/二维列表中的重复行方法
2019/01/23 Python
使用OpenCV实现仿射变换—平移功能
2019/08/29 Python
django模型类中,null=True,blank=True用法说明
2020/07/09 Python
python单元测试框架pytest的使用示例
2020/10/07 Python
局部内部类是否可以访问非final变量?
2013/04/20 面试题
Prototype中如何为一个元素添加一个方法
2014/12/08 面试题
会计专业毕业自荐书范文
2014/02/08 职场文书
离职报告格式
2014/11/04 职场文书
教师工作决心书
2015/02/04 职场文书
关爱留守儿童捐款倡议书
2015/04/27 职场文书
队列队形口号
2015/12/25 职场文书
导游词之广东佛山(南风古灶)
2019/09/24 职场文书
CSS布局之浮动(float)和定位(position)属性的区别
2021/09/25 HTML / CSS
关于SpringBoot 使用 Redis 分布式锁解决并发问题
2021/11/17 Redis
MySQL运行报错:“Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggre”解决方法
2022/06/14 MySQL