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实现在小方框中浏览大图的代码
Aug 14 Javascript
javascript onkeydown,onkeyup,onkeypress,onclick,ondblclick
Feb 04 Javascript
用jquery实现学校的校历(asp.net+jquery ui 1.72)
Jan 01 Javascript
iframe的父子窗口之间的对象相互调用基本用法
Sep 03 Javascript
JavaScript基础知识及常用方法总结
Jan 10 Javascript
JS数组操作(数组增加、删除、翻转、转字符串、取索引、截取(切片)slice、剪接splice、数组合并)
May 20 Javascript
Mongoose学习全面理解(推荐)
Jan 21 Javascript
vue-router 组件复用问题详解
Jan 22 Javascript
小试SVG之新手小白入门教程
Jan 08 Javascript
微信小程序canvas绘制圆角base64图片的实现
Aug 18 Javascript
JS实现关闭小广告特效
Jan 29 Javascript
JS数组属性去重并校验重复数据
Jan 10 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操作mysql函数详解,mysql和php交互函数
2011/05/19 PHP
php连接函数implode与分割explode的深入解析
2013/06/26 PHP
php缩放gif和png图透明背景变成黑色的解决方法
2014/10/14 PHP
PHP的介绍以及优势详细分析
2019/09/05 PHP
PHP+ajax实现上传、删除、修改单张图片及后台处理逻辑操作详解
2020/02/12 PHP
javascript实现的listview效果
2007/04/28 Javascript
FLASH 广告之外的链接
2008/12/16 Javascript
Js从头学起(基本数据类型和引用类型的参数传递详细分析)
2012/02/16 Javascript
javascript学习笔记(二十) 获得和设置元素的特性(属性)
2012/06/20 Javascript
利用jquery包将字符串生成二维码图片
2013/09/12 Javascript
jquery实现图片灯箱明暗的遮罩效果
2013/11/15 Javascript
jquery提交form表单时禁止重复提交的方法
2014/02/13 Javascript
Nodejs极简入门教程(一):模块机制
2014/10/25 NodeJs
JS实现简单的右下角弹出提示窗口完整实例
2016/06/21 Javascript
浅谈JS继承_借用构造函数 &amp; 组合式继承
2016/08/16 Javascript
JavaScript实现汉字转换为拼音的库文件示例
2016/12/22 Javascript
微信小程序实现点击图片放大预览
2019/10/21 Javascript
[03:04]2018年国际邀请赛典藏宝瓶&莱恩声望物品展示 片尾有彩蛋
2018/06/04 DOTA
python使用multiprocessing模块实现带回调函数的异步调用方法
2015/04/18 Python
python requests 使用快速入门
2017/08/31 Python
python实现两个文件合并功能
2018/04/01 Python
nohup后台启动Python脚本,log不刷新的解决方法
2019/01/14 Python
在Python中表示一个对象的方法
2019/06/25 Python
python 寻找离散序列极值点的方法
2019/07/10 Python
Python将string转换到float的实例方法
2019/07/29 Python
Python numpy多维数组实现原理详解
2020/03/10 Python
HTML5 File API改善网页上传功能
2009/08/19 HTML / CSS
加拿大最大的相机店:Henry’s
2017/05/17 全球购物
什么是makefile? 如何编写makefile?
2012/08/08 面试题
就业协议书样本
2014/08/20 职场文书
课内比教学心得体会
2014/09/09 职场文书
党员教师学习党的群众路线教育实践活动心得体会
2014/10/31 职场文书
优秀教师推荐材料
2014/12/16 职场文书
2014年仓库管理工作总结
2014/12/17 职场文书
2015年教研组工作总结
2015/05/04 职场文书
2015年领导干部廉洁自律工作总结
2015/05/26 职场文书