`

Jquery1.4.3源码分析(一)

阅读更多
Jquery是站在开发者的角度去考虑问题,在使用Js的时候,大部分时间都是对Dom元素进行操作,比如修改元素的属性,修改内容,修改CSS等。但是取Dom元素的,如getElementsByTag,有可能会取到一些Dom元素的集合,而又正好要这个集合的所有的元素都要进行同样的操作。如果只有一个元素,完全可以看作只有一个元素的集合。

这样只要对这个集合进行操作,就会对集合的每个元素都进行操作。jQuery就是基于这个集合而提供了众多的实用方法,包含了日常开发所需要的功能。对于这个集合,我们称为jquery对象。

我们可以通过$(params)或jquery(params)来生成Jquery对象。在Jquery文档中提供了四种方式:jQuery(expression,[context]),jQuery(html),jQuery(elements),jQuery(callback)四种构寻jquery对象的方式。其实Jquery的参数可以是任何的元素,如空参数,都能构成jquery对象。

那么jquery是如何实现的呢?

var jQuery = function( selector, context ) {
//jQuery对象其实就是jQuery.fn.init函数生成的对象,不是通过new jQuery生成对象。
		return new jQuery.fn.init( selector, context );
	},

现在我们看一下jQuery.fn.init函数的实现:
init: function( selector, context ) {
		var match, elem, ret, doc;
        //处理传空值的情况
		// Handle $(""), $(null), or $(undefined)
		if ( !selector ) {
			return this;
		}
        //// 第一种情况 Handle $(DOMElement)单个Dom 元素,忽略上下文
		// Handle $(DOMElement)
		if ( selector.nodeType ) {
			this.context = this[0] = selector;
			this.length = 1;
			return this;
		}
		//如果selector为'body',而且只存在一次,那么使用优化找到它
		// The body element only exists once, optimize finding it
		if ( selector === "body" && !context && document.body ) {
			this.context = document;
			this[0] = document.body;
			this.selector = "body";
			this.length = 1;
			return this;
		}
        //
		// Handle HTML strings
		if ( typeof selector === "string" ) {
			// Are we dealing with HTML string or an ID?
			match = quickExpr.exec( selector );

			// Verify a match, and that no context was specified for #id
			if ( match && (match[1] || !context) ) {
                 //处理$(html) -> $(array)
				// HANDLE: $(html) -> $(array)
				if ( match[1] ) {
					doc = (context ? context.ownerDocument || context : document);
                     //如果只有个一个字符串传递进来,那么直接使用createElement创建元素,跳过剩下的部分
					// If a single string is passed in and it's a single tag
					// just do a createElement and skip the rest
                    //如

					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 {
                        //$('<div>sgsgsg
</div>')解析字符串
						ret = jQuery.buildFragment( [ match[1] ], [ doc ] );
						selector = (ret.cacheable ? ret.fragment.cloneNode(true) : ret.fragment).childNodes;
					}
					//把创建出来的element放进Jquery对象集合中.
					return jQuery.merge( this, selector );
					
				// HANDLE: $("#id") //处理$("#id")
				} else {
					elem = document.getElementById( match[2] );

					// Check parentNode to catch when Blackberry 4.6 returns
					// nodes that are no longer in the document #6963
					if ( elem && elem.parentNode ) {
						// Handle the case where IE and Opera return items
						// by name instead of ID
						if ( elem.id !== match[2] ) {
							return rootjQuery.find( selector );
						}

						// Otherwise, we inject the element directly into the jQuery object
						this.length = 1;
						this[0] = elem;
					}

					this.context = document;
					this.selector = selector;
					return this;
				}

			// HANDLE: $("TAG") 处理$('p')
			} else if ( !context && !rnonword.test( selector ) ) {
				this.selector = selector;
				this.context = document;
				selector = document.getElementsByTagName( selector );
				return jQuery.merge( this, selector );

			// HANDLE: $(expr, $(...))   处理$(expr, [context])==$(content).find(expr)
			} else if ( !context || context.jquery ) {
				return (context || rootjQuery).find( selector );

			// HANDLE: $(expr, context)
			// (which is just equivalent to: $(context).find(expr)
			} else {
				return jQuery( context ).find( selector );
			}

		// HANDLE: $(function) 处理$(function)
		// Shortcut for document ready
		} else if ( jQuery.isFunction( selector ) ) {
			return rootjQuery.ready( selector );
		}

		if (selector.selector !== undefined) {
			this.selector = selector.selector;
			this.context = selector.context;
		}
      // 处理$(elements)
		return jQuery.makeArray( selector, this );
	}

上面的可以看出$(xx)或Jquery(xx)得到不是真正的jQuery函数生成的对象,而是jQuery.fn.init函数生成的对象。也是就是jQuery的对象继承的是jQuery.fn.init的原型。jQuery.fn = jQuery.prototype={..}。我们基本上不用new jQuery(xx),而是直接jQuery(xx),就是采用了new jQuery(xx),先生成jQuery函数的对象,把原型中的继承下来,返回的也是jQuery.fn.init函数生成的对象。而jQuery函数的对象也抛弃了。可见给jQuery.prototype上增加方法的意义不大。同时也可以看出采用new jQuery(xx)的效率更低。jQuery.fn.init是通过jQuery.fn.init.prototype = jQuery.fn;来获得的。在扩展jQuery的时候,只要把相关的函数extend到jQuery.fn就可以了。

jQuery.fn.init负责对传的参数进行分析然后生成jQuery对象。它把第一个参数分成四种情况:




 从上面的代码和上表中,我们也可以看出构建jquery对象就是往jquery对象的集合中添加元素(一般都应该是dom元素)。添加的元素有两种形式:

  一是单个元素,可能通过直接的dom元素的传参形式,还可以通过#id从dom文档中找元素。

  二是集合,如jquery对象,还有数组,还有通过CSS Selector找到的Dom集合等Array-Like。

  上表仅仅是分析传入的参数的类型,它是怎么做呢?它实现CSS1~CSS3的兼容的Selector的查寻器的功能。通过jQuery().find(selector);来进行分析String并查找到符合传入的Selector语法的Dom文档树中的元素集合。

 它实现了把html的字符串转换成Dom元素节点的集合。这个是通过jQuery.clean([match[1]], context);来实现的。

  它实现DomReady的jQuery对象的统一入口,我们可以通过$(fn)要注册domReady的监听函数。所有的调用jQuery实现的功能代码都应该在domReady之后才运行。$(fn)是所有的应用开发中的功能代码的入口。它支持任意多的$(fn)注册。其是通过return jQuery(document)[jQuery.fn.ready ? "ready" : "load"](selector);来完成的。

  找到元素之后就是构建集合了,就是通过this.setArray(jQuery.makeArray(selector));来构建jquery对象内部的集合。

在jQuery.fn.init函数中,最终的结果是把Dom元素放到jQuery对象的集合,我们可以传入单个Dom元素或Dom元素集合直接把其存到jQuery对象的集合。但是如果第一个参数是string类型的话,如#id就要把Dom文档树去查找。对于html的片断就得生成Dom元素。我们再进一步,传入的单个Dom元素或Dom元素集合参数又是从那里来的?我们可以通过Dom元素的直接或间接的查找元素的方式。

  这一部分首先分析如何从html的片断就得生成Dom元素,然后分析jQuery是如何通过直接或间接的方式在在Dom树中找到dom元素,第三就是分析基于CSS1~CSS3的CSS selector。
3.1生成Dom元素

  Init方法中通过ret = jQuery.buildFragment( [ match[1] ], [ doc ] );来实现把html片断转换成Dom元素,这是一个静态方法:
jQuery.buildFragment = function( args, nodes, scripts ) {
	var fragment, cacheable, cacheresults,
		doc = (nodes && nodes[0] ? nodes[0].ownerDocument || nodes[0] : document);
     //只有缓存小于0.5K的字符串,有些元素不能缓存啊 
	// Only cache "small" (1/2 KB) strings that are associated with the main document
	// Cloning options loses the selected state, so don't cache them
	// IE 6 doesn't like it when you put <object> or <embed> elements in a fragment
	// Also, WebKit does not clone 'checked' attributes on cloneNode, so don't cache
	if ( args.length === 1 && typeof args[0] === "string" && args[0].length < 512 && doc === document &&
		!rnocache.test( args[0] ) && (jQuery.support.checkClone || !rchecked.test( args[0] )) ) {

		cacheable = true;
		cacheresults = jQuery.fragments[ args[0] ];
		if ( cacheresults ) {
			if ( cacheresults !== 1 ) {
				fragment = cacheresults;
			}
		}
	}

	if ( !fragment ) {
		fragment = doc.createDocumentFragment();
		jQuery.clean( args, doc, fragment, scripts );
	}

	if ( cacheable ) {
		jQuery.fragments[ args[0] ] = cacheresults ? fragment : 1;
	}

	return { fragment: fragment, cacheable: cacheable };
};

jQuery.clean( args, doc, fragment, scripts );
 // 把html转换成Dom元素,elems多个html string 的数组    
	clean: function( elems, context, fragment, scripts ) {
		context = context || document; ;//默认的上下文是document

        //在IE中!context.createElement行不通,因为它返回对象类型
        //在IE中 typeof document.createElement返回object, firefox返回function
		if ( typeof context.createElement === "undefined" ) {
        //这里支持context为jQuery对象,取第一个元素。
			context = context.ownerDocument || context[0] && context[0].ownerDocument || document;
		}

		var ret = [];

		for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
			if ( typeof elem === "number" ) {
				elem += "";// 把int 转换成string的最高效的方法
			}

			if ( !elem ) {
				continue;
			}

			// Convert html string into DOM nodes

			if ( typeof elem === "string" && !rhtml.test( elem ) ) {
				elem = context.createTextNode( elem );//如果elem为文本,不包含元素标签

			} else if ( typeof elem === "string" ) {
				// Fix "XHTML"-style tags in all browsers
                //修改元素使得符合xhtml的标准
				elem = elem.replace(rxhtmlTag, "<$1></$2>");

				// Trim whitespace, otherwise indexOf won't work as expected
                //取得元素标签
				var tag = (rtagName.exec( elem ) || ["", ""])[1].toLowerCase(),
                    //判断是否要进行修正option, legend,thead,tr,td, col,area
					wrap = wrapMap[ tag ] || wrapMap._default,
					depth = wrap[0],
					div = context.createElement("div");

				// Go to html and back, then peel off extra wrappers
				div.innerHTML = wrap[1] + elem + wrap[2];

				// Move to the right depth   转到正确的深度,对于[1, "<table>","</table>"],div=<table>
				while ( depth-- ) {
					div = div.lastChild;
				}

				// fragments去掉IE对<table>自动插入的<tbody>
				if ( !jQuery.support.tbody ) {
                    // 第一种情况:tags以<table>开头但没有<tbody>。在IE中生成的元素中可能会自动
                    // 加的<tbody> 第二种情况:thead|tbody|tfoot|colg|cap为tags,
                    // 那wrap[1] == "<table>" .tbody不一定是tbody,也有可能是thead等等

					// String was a <table>, *may* have spurious <tbody>
					var hasBody = rtbody.test(elem),
						tbody = tag === "table" && !hasBody ?
							div.firstChild && div.firstChild.childNodes :

							// String was a bare <thead> or <tfoot>
							wrap[1] === "<table>" && !hasBody ?
								div.childNodes :
								[];
                    // 除去<tbody>                     
					for ( var j = tbody.length - 1; j >= 0 ; --j ) {
						if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length ) {
							tbody[ j ].parentNode.removeChild( tbody[ j ] );
						}
					}

				}
                 //使用innerHTML,IE会去开头的空格节点的,加上去掉的空格节点                
				// IE completely kills leading whitespace when innerHTML is used
				if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) {
					div.insertBefore( context.createTextNode( rleadingWhitespace.exec(elem)[0] ), div.firstChild );
				}

				elem = div.childNodes;
			}

			if ( elem.nodeType ) {
				ret.push( elem ); //elem放进集合里面
			} else {
				ret = jQuery.merge( ret, elem );
			}
		}

		if ( fragment ) {
			for ( i = 0; ret[i]; i++ ) {
				if ( scripts && jQuery.nodeName( ret[i], "script" ) && (!ret[i].type || ret[i].type.toLowerCase() === "text/javascript") ) {
					scripts.push( ret[i].parentNode ? ret[i].parentNode.removeChild( ret[i] ) : ret[i] );
				
				} else {
					if ( ret[i].nodeType === 1 ) {
						ret.splice.apply( ret, [i + 1, 0].concat(jQuery.makeArray(ret[i].getElementsByTagName("script"))) );
					}
					fragment.appendChild( ret[i] );
				}
			}
		}

		return ret;
	},


 在上面的代码中,我们可以看出对于elems, context的参数的支持是多种形式的,elems可以为(类)数组的形式,还可以采用对象的形式。数组中的元素或对象的属性可以是混合形的,如string,ojbect,甚至(类)数组的形式。对于数字类型,会转换在string形,除string形之外的都放入返回的数组中,当然对于集合的形式,那就会取集合中每个元素。

  对于string的形式就转换成Dom元素的形式,之后存到返回的数组中。这是这个函数的主要任务。对于把html转换成Dom元素,这里采用innerHTML把html挂到Dom文档树中。这样就转换成了Dom元素。

  有些html标签片断是有约束的,比如<td>xx</td>,它必须存在table的tr中,也就是说在要进行html的标签片断的修正。这也是上面的代码处理的重点。



  • 大小: 63.4 KB
  • 大小: 57.3 KB
分享到:
评论

相关推荐

    jquery 1.4.3

    jQuery 1.4.3是JavaScript库的一个重要版本,它以其高效、简洁的语法和强大的功能深受开发者喜爱。在这个版本中,jQuery进一步优化了性能,增加了新的API特性,并对已有的函数进行了改进,使得网页的动态交互变得...

    jquery-1.4.3.js+jquery-1.7.2.js包下载

    《jQuery 1.4.3与1.7.2:两个关键版本的比较与应用》 jQuery是一款广泛使用的JavaScript库,极大地简化了JavaScript的DOM操作、事件处理、动画设计和Ajax交互。本篇文章将深入探讨jQuery 1.4.3和1.7.2这两个重要...

    jQuery1.4.3 正式版

    总的来说,jQuery1.4.3是一个强大且稳定的JavaScript库,它的出现大大提高了前端开发的效率,降低了跨浏览器兼容性的困扰。通过学习和熟练运用jQuery,开发者能够构建出更加丰富、交互性更强的网页应用。对于初学者...

    jquery1.43源码分析(核心部分)[收集].pdf

    《jQuery 1.4.3 源码分析——核心部分》 jQuery 是一个广泛使用的JavaScript库,它极大地简化了JavaScript的DOM操作、事件处理、动画制作和Ajax交互。在jQuery 1.4.3版本中,其核心部分主要包括选择器引擎、DOM操作...

    (jquery最新版)jquery-1.4.3.js

    **描述中提到的"jquery-1.4.3.rar"是一个压缩文件,包含两个文件:** 1. **jquery-1.4.3.js**:这是未压缩的jQuery库,代码可读性强,便于调试和理解。在开发环境中,通常使用这种版本。 2. **jquery-1.4.3.min.js**...

    jquery EasyUI 1.4.3奇葩案例代码

    **jQuery EasyUI 1.4.3:一个奇葩案例代码详解** jQuery EasyUI 是一个基于 jQuery 的前端框架,它提供了一系列的组件,如表格、下拉框、对话框、菜单等,帮助开发者快速构建用户界面。这个“jQuery EasyUI 1.4.3...

    jQuery EasyUI 1.4.3 API

    jQuery EasyUI 是一个基于 jQuery 的前端框架,它简化了网页用户界面开发,提供了一系列易于使用的组件,如对话框、表格、菜单、按钮等。1.4.3 版本是该框架的一个稳定版本,提供了丰富的功能和改进。下面将详细阐述...

    jquery1.43源码分析(核心部分)

    ### jQuery 1.4.3 源码分析(核心部分) #### 一、引言 随着 Web 技术的不断发展,JavaScript 成为了前端开发中不可或缺的一部分。而在众多 JavaScript 库中,jQuery 几乎成为了网页开发的标准工具之一。本文将...

    JQuery_API 1.4.3通用版

    **jQuery API 1.4.3通用版**是JavaScript库jQuery的一个特定版本,它提供了丰富的功能,便于开发者进行网页交互和DOM操作。这个版本在2010年发布,是许多开发者信赖的稳定版本,适用于各种项目。HTML格式的文档意味...

    jquery1.43源码分析之工具方法.doc

    通过对`jQuery 1.4.3`源码中的工具方法的深入分析,我们可以看到这些工具方法不仅为用户提供了一种高效便捷的方式来处理常见的编程任务,同时也为后续版本的设计提供了很好的启示。无论是数组类型的判断还是数组遍历...

    jQuery EasyUI 1.4.3 版 API 中文版.exe

    jQuery EasyUI 1.4.3 版 API 中文版.

    ajax框架jquery_1.4.3包含压缩版和未压缩版

    jQuery 1.4.3 是该库的一个稳定版本,发布于2010年。这个版本包含了多项改进和修复,提高了性能和兼容性。在这一版本中,jQuery优化了许多核心函数,比如选择器引擎、DOM操作和事件处理,使其运行更高效。此外,它还...

    jquery-1.4.3.min[1].js

    jquery-1.4.3.min[1].js

    jquery-easyui-1.4.3

    总的来说,jQuery EasyUI 1.4.3是一个功能全面、易于使用的前端框架,它极大地简化了Web开发过程,提高了开发效率,是构建现代Web应用的理想选择。无论你是初学者还是经验丰富的开发者,都能从中受益,快速打造出...

    jQuery EasyUI 1.4.3 版 API 中文版

    jQuery EasyUI 是一个基于 jQuery 的前端框架,它提供了一系列易于使用的组件,用于快速构建用户界面。1.4.3 版本是这个框架的一个稳定版本,包含了许多改进和优化,适用于开发人员进行Web应用程序的开发。API中文版...

    JQuery_1.4.3

    JQuery_1.4.3 非常流行的ajax程序库 1.4.3是最新版的jQuery程序库,

    javamail-1.4.3源码

    通过分析JavaMail 1.4.3的源码,开发者可以学习如何正确处理邮件的生命周期,理解邮件协议的工作原理,以及如何在实际项目中有效地使用JavaMail API。同时,源码还包含了各种错误处理和异常处理的示例,这对于编写...

    GISpace动态标绘演示系统1.4.3源码

    GISpace动态标绘演示系统1.4.3源码是一个专为地理信息系统(GIS)设计的应用,用于实现动态的地理信息标绘功能。这个版本在上一个版本的基础上进行了升级,特别是在标绘符号编辑方面,增加了对控制点的操作,使得...

Global site tag (gtag.js) - Google Analytics