javascript笔记:String的replace(续),由正则表达式到jQuery选择器

  我在上一篇博客里谈到了javascript里面的String类的 replace方法的一些问题,今天我真正的学习了javascript里的正则表达式的用法(以前总是不屑学习这个技术,现在发现编程里字符处理的技术还是相当的重要,应用领域很广泛而且也有一定难度,比如jQuery源码里面就有很多正则表达式的使用),对于String类里 s.replace(regex,function(){})的理解更清晰,以前不清晰的原因是没有学习好正则表达式。我写了下面的测试代码:

//下面代码请在装有firebug的firefox里面运行
function myReplace()
{
	var reg = /%[1-4]/g;
	var data = "And the %1 want to know whose %2 you %3";
	
	while(1==1)
	{
		var val = reg.exec(data);
		if (val == null)
		{
			break;
		}else{
			console.log(val);
		}
	}
	
	reg = /CJ[0-9]{2}/g;
	data = 'CJ9080,CJ8976,CJ12919,CJ8765';
	while(1==1)
	{
		var val = reg.exec(data);
		if (val == null)
		{
			break;
		}else{
			console.log(val);
		}
	}
	
}
myReplace();

结果如下:

  其实String类的s.replace(regex,function(){})用法就是了Regexexec()方法,只不过当正则式为[1-4]这样格式的时候,replace方法会在遍历字符串时候把里面的1-4的值都取出来,放到functionargument[1]里面。

  今天抽时间读了一下jQuery的源代码,jQuery说白了就是一个选择器,例如我们常看到这样的写法:

  

jQuery('#userId').val();
jQuery('div').text();

  

上面代码就是在使用jQuery选择器,jQuery选择器实现了下列四个方法:

jQuery( expression, context ) 
Returns: jQuery
这个函数接收一个CSS选择器的字符串,然后用这个字符串去匹配一组元素。 
This function accepts a string containing a CSS selector which is then used to match a set of elements.
jQuery( html, ownerDocument ) 
Returns: jQuery
根据HTML原始字符串动态创建Dom元素.
Create DOM elements on-the-fly from the provided String of raw HTML.
jQuery( elements ) 
Returns: jQuery
将一个或多个Dom对象封装jQuery函数功能(即封装为jQuery包装集)
Wrap jQuery functionality around a single or multiple DOM Element(s).
jQuery( callback ) 
Returns: jQuery
$(document).ready()的简写方式
A shorthand for $(document).ready().
上面摘选自jQuery官方手册。

在选择器方法里面使用到了这样一个正则表达式:

quickExpr = /^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/

这个正则表达式都是在我们传入jQuery里面第一个参数是string时候会调用,具体点就是当你不是传入$(""), $(null), $(undefined)或者$(DOMElement)时候就会使用到这个正则表达式。因此我想在这里好好分析下这两个正则表达式。

首先补充下正则表达式的基础知识:

元字符

描述

.

匹配任何单个字符。例如正则表达式r.t匹配这些字符串:ratrutr t,但是不匹配root

$

匹配行结束符。例如正则表达式weasel$ 能够匹配字符串"He's a weasel"的末尾 
但是不能匹配字符串"They are a bunch of weasels."

^

匹配一行的开始。例如正则表达式^When in能够匹配字符串"When in the course of human events"的开始,但是不能匹配"What and When in the"

*

匹配0或多个正好在它之前的那个字符。例如正则表达式。*意味着能够匹配任意数量的任何字符。

\

这是引用符,用来将这里列出的这些元字符当作普通的字符来进行匹配。例如正则表达式\$被用来匹配美元符号,而不是行尾,类似的,正则表达式\.用来匹配点字符,而不是任何字符的通配符。

[ ] 
[c1-c2] 
[^c1-c2]

匹配括号中的任何一个字符。例如正则表达式r[aou]t匹配ratrotrut,但是不匹配ret。可以在括号中使用连字符-来指定字符的区间,例如正则表达式[0-9]可以匹配任何数字字符;还可以制定多个区间,例如正则表达式[A-Za-z]可以匹配任何大小写字母。另一个重要的用法是排除,要想匹配除了指定区间之外的字符——也就是所谓的补集——在左边的括号和第一个字符之间使用^字符,例如正则表达式[^269A-Z] 将匹配除了269和所有大写字母之外的任何字符。

\< \>

匹配词(word)的开始(\<)和结束(\>)。例如正则表达式\<the\>能够匹配字符串"for the wise"中的"the",但是不能匹配字符串"otherwise"中的"the"。注意:这个元字符不是所有的软件都支持的。

\( \)

\( \) 之间的表达式定义为group),并且将匹配这个表达式的字符保存到一个临时区域(一个正则表达式中最多可以保存9个),它们可以用 \1 \9 的符号来引用。

|

将两个匹配条件进行逻辑Or)运算。例如正则表达式(him|her) 匹配"it belongs to him""it belongs to her",但是不能匹配"it belongs to them."。注意:这个元字符不是所有的软件都支持的。

+

匹配1或多个正好在它之前的那个字符。例如正则表达式9+匹配999999等。注意:这个元字符不是所有的软件都支持的。

?

匹配01个正好在它之前的那个字符。注意:这个元字符不是所有的软件都支持的。

{i} 
{i,j}

匹配指定数目的字符,这些字符是在它之前的表达式定义的。例如正则表达式A[0-9]{3} 能够匹配字符"A"后面跟着正好3个数字字符的串,例如A123A348等,但是不匹配A1234。而正则表达式[0-9]{4,6} 匹配连续的任意4个、5个或者6个数字字符。注意:这个元字符不是所有的软件都支持的。

\s

匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [?\f\n\r\t\v]

\w

匹配包括下划线的任何单词字符。等价于’[A-Za-z0-9_]‘

\W

匹配任何非单词字符。等价于 ‘[^A-Za-z0-9_]‘

正则表达式quickExpr = /^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/,可以由 | 分为两个部分,前一个部分是^[^<]*(<[\w\W]+>)[^>]*$,这个有开始符号^和结束符号$,该表达式按顺序分析:

1.     [^<]*----标示字符的头部可以是除了<的任意字符或者是干脆没有字符

2.     (<[\w\W]+>)-----这个表示字符串里要包含用<>包含的字符,例如<p>,<div>等等都是符合要求的

3.     [^>]*----字符串尾部是除了>的任意字符或者没有字符

由上可知表达式^[^<]*(<[\w\W]+>)[^>]*$的意思是字符串里面一定要包含被尖括号包含的字符也就是html代码。

正则表达式的后半部分是:^#([\w-]+)$,这个就比较简单了,它的含义是匹配带上#号的任意字符。

那么整个表达式的含义就是匹配HTML标记和ID表达式。下面我做了针对这个表达式的测试,代码如下:

 

function regrexFtn()
{
	var quickExpr = /^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/;
	var data = "#userId";
	
	console.log(quickExpr.exec(data));
	
	data = "<span>javascript jquery</span>";
	console.log(quickExpr.exec(data));
	
	data = "start<span>javascript jquery</span>end";
	console.log(quickExpr.exec(data));
	
	data = "div .red";
	console.log(quickExpr.exec(data));
	
	data = "javascript jquery";
	console.log(quickExpr.exec(data));
	
	data = "div";
	console.log(quickExpr.exec(data));
	
	data = ".odd";
	console.log(quickExpr.exec(data));
	
}

regrexFtn();

结果如下:

我在读jQuery选择器源码时候,对于这个表达式的正确理解是我的难点,所以我单独研究下这个表达式,现在清楚了它的功能,那么我在读取选择器的源码就简单多了。下面是jquery-1.4.1.js的选择器的源码:

	init: function( selector, context ) {
		var match, elem, ret, doc;

		// Handle $(""), $(null), or $(undefined)
		if ( !selector ) {
			return this;
		}

		// Handle $(DOMElement)
		if ( selector.nodeType ) {
			this.context = this[0] = selector;
			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) ) {

				// HANDLE: $(html) -> $(array)
				if ( match[1] ) {
					doc = (context ? context.ownerDocument || context : document);

					// 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 {
						ret = buildFragment( [ match[1] ], [ doc ] );
						selector = (ret.cacheable ? ret.fragment.cloneNode(true) : ret.fragment).childNodes;
					}

				// HANDLE: $("#id")
				} else {
					elem = document.getElementById( match[2] );

					if ( elem ) {
						// 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")
			} else if ( !context && /^\w+$/.test( selector ) ) {
				this.selector = selector;
				this.context = document;
				selector = document.getElementsByTagName( selector );

			// HANDLE: $(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)
		// 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;
		}

		return jQuery.isArray( selector ) ?
			this.setArray( selector ) :
			jQuery.makeArray( selector, this );
	},

源码解析:

参数说明:selector:选择器的符号,可以是任意数据类型。

         Context:上下文,指定在文档DOM中那个节点下进行查询,默认值是document

1.     如果我们写的选择器是这样jQuery (""),jQuery (null), jQuery (undefined),那么执行的代码是:

        if ( !selector ) {

            return this;

        }

直接返回一个jQuery对象。

2.     如果我们写的选择器是jQuery(document.getElementById('#d01'));也就是jQuery里的参数是一个DOM元素,那么执行的代码是:

		if ( selector.nodeType ) {
			this.context = this[0] = selector;//把当前的DOM元素存入到jQuery类的数组中
			this.length = 1;//设置jQuery类的数组长度,方便以后遍历访问
			return this;//返回jQuery对象
		}

3.     如果我们在jQuery选择器里面传入的是字符串,就是这样的形式jQuery('..'),那么jQuery都会执行这段代码:

		if ( typeof selector === "string" ) {
			// Are we dealing with HTML string or an ID?
			match = quickExpr.exec( selector );

下面代码如何运行都是根据match这个变量所决定的,根据我上面测试的结果来看看这段代码。

4.jQuery('#userIds'),jQuery里面的参数是一个ID值,match = quickExpr.exec( selector );的执行结果是:

那么if ( match && (match[1] || !context) )里面的match && (match[1] || !context)等价于(true && (false || true))结果就是false了。接下来执行的代码是:

				} else {
					elem = document.getElementById( match[2] );//获取DOM元素

					if ( elem ) {
						//获取该元素确保元素存在,因为在ie或是Opera
						//浏览器里面这个方法可能是根据name而不是ID
						if ( elem.id !== match[2] ) {
							return rootjQuery.find( selector );
						}

						//否则就将elem放入jQuery类的数组里,直接返回jQuery对象s
						this.length = 1;// 设置jQuery类的数组长度,方便以后遍历访问

						this[0] = elem;// 把当前的DOM元素存入到jQuery类的数组中
					}

					this.context = document;//设置jQuery对象的上下文属性
					this.selector = selector;
					return this;//返回jQuery对象s
				}

5.     jQuery(‘div’),jQuery参数是html标签,那么match = quickExpr.exec( selector );执行的结果就是null了,这时候程序会执行下面代码:

			} else if ( !context && /^\w+$/.test( selector ) ) {//正则表达式是检测传入参数是不是符合标签规则的字符串
				this.selector = selector;
				this.context = document;
				selector = document.getElementsByTagName( selector );
			}
然后代码会走到这里:
else {
				return jQuery( context ).find( selector );
			}

但是这时候程序并没有跳出代码,而是走到了

		return jQuery.isArray( selector ) ?
			this.setArray( selector ) :
			jQuery.makeArray( selector, this );

这个蛮奇怪的。

6.     如果jQuery选择器这么写jQuery('start<span>javascript jquery</span>end')或者jQuery('<span>javascript jquery</span>s')match = quickExpr.exec( selector );执行的结果如下:

程序执行的代码是:

			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;
					}

这段代码里操作的都是match[1],可以看出,这里使用到的结果是<span>javascript jquery</span>,也就是代码只是对html代码进行操作。

7.     当我们写这样的选择器时候jQuery('div .red'),这时候match = quickExpr.exec( selector );执行的结果是null,代码最终会执行到:

quickExpr.exec( selector );执行的结果是null,代码最终会执行到:
			} else if ( !context || context.jquery ) {
				return (context || rootjQuery).find( selector );//默认调用document.find()方法。

posted @ 2011-09-22 01:22  夏天的森林  阅读(8089)  评论(8编辑  收藏  举报