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 相关文章推荐
js列举css中所有图标的实现代码
Jul 04 Javascript
caller和callee的区别介绍及演示结果
Mar 10 Javascript
Ext4.2的Ext.grid.plugin.RowExpander无法触发事件解决办法
Aug 15 Javascript
深入分析JSONP跨域的原理
Dec 10 Javascript
兼容主流浏览器的JS复制内容到剪贴板
Dec 12 Javascript
跟我学习JScript的Bug与内存管理
Nov 18 Javascript
JavaScript设计模式开发中组合模式的使用教程
May 18 Javascript
javascript判断firebug是否开启的方法
Nov 23 Javascript
详解vue中computed 和 watch的异同
Jun 30 Javascript
React-Native左右联动List的示例代码
Sep 21 Javascript
vue-cli下的vuex的简单Demo图解(实现加1减1操作)
Feb 26 Javascript
JavaScript如何判断对象有某属性
Jul 03 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配置文件中最常用四个ini函数
2007/03/19 PHP
PHP和Mysqlweb应用开发核心技术 第1部分 Php基础-1 开始了解php
2011/07/03 PHP
php中3种方法统计字符串中每种字符的个数并排序
2012/08/27 PHP
php中使用preg_match_all匹配文章中的图片
2013/02/06 PHP
laravel返回统一格式错误码问题
2019/11/04 PHP
php5.3/5.4/5.5/5.6/7常见新增特性汇总整理
2020/02/27 PHP
基于jquery的让textarea自适应高度的插件
2010/08/03 Javascript
DOM和XMLHttpRequest对象的属性和方法整理
2012/01/04 Javascript
左侧是表头的JS表格控件(自写,网上没有的)
2013/06/04 Javascript
Bootstrap3制作自己的导航栏
2016/05/12 Javascript
使用jquery提交form表单并自定义action的方法
2016/05/25 Javascript
深入浅析JavaScript函数前面的加号和叹号
2016/07/09 Javascript
详解用node.js实现简单的反向代理
2017/06/26 Javascript
微信小程序获取手机网络状态的方法【附源码下载】
2017/12/08 Javascript
在vue中实现嵌套页面(iframe)
2020/07/30 Javascript
Python程序设计入门(4)模块和包
2014/06/16 Python
python修改list中所有元素类型的三种方法
2018/04/09 Python
tensorflow学习笔记之mnist的卷积神经网络实例
2018/04/15 Python
django传值给模板, 再用JS接收并进行操作的实例
2018/05/28 Python
pytorch查看torch.Tensor和model是否在CUDA上的实例
2020/01/03 Python
Python中if有多个条件处理方法
2020/02/26 Python
Python递归函数特点及原理解析
2020/03/04 Python
Pytest如何使用skip跳过执行测试
2020/08/13 Python
python sleep和wait对比总结
2021/02/03 Python
css3打造一款漂亮的卡哇伊按钮
2013/03/20 HTML / CSS
HTML5 用动画的表现形式装载图像
2016/03/08 HTML / CSS
friso美素佳儿官方海外旗舰店:荷兰原产原罐
2017/07/03 全球购物
药物学专业学生的自我评价
2013/10/27 职场文书
六查六看剖析材料
2014/02/15 职场文书
制药工程专业职业生涯规划范文
2014/03/10 职场文书
优秀的个人求职信范文
2014/05/09 职场文书
cf战队宣传语
2015/07/13 职场文书
golang通过递归遍历生成树状结构的操作
2021/04/28 Golang
微信小程序中使用vant框架的具体步骤
2022/02/18 Javascript
分享提高 Python 代码的可读性的技巧
2022/03/03 Python
Python如何将list中的string转换为int
2022/07/15 Ruby