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(){})用法就是了Regex的exec()方法,只不过当正则式为[1-4]这样格式的时候,replace方法会在遍历字符串时候把里面的1-4的值都取出来,放到function的argument[1]里面。
今天抽时间读了一下jQuery的源代码,jQuery说白了就是一个选择器,例如我们常看到这样的写法:
jQuery('#userId').val(); jQuery('div').text();
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匹配这些字符串:rat、rut、r t,但是不匹配root。 |
$ |
匹配行结束符。例如正则表达式weasel$ 能够匹配字符串"He's a weasel"的末尾 |
^ |
匹配一行的开始。例如正则表达式^When in能够匹配字符串"When in the course of human events"的开始,但是不能匹配"What and When in the" |
* |
匹配0或多个正好在它之前的那个字符。例如正则表达式。*意味着能够匹配任意数量的任何字符。 |
\ |
这是引用符,用来将这里列出的这些元字符当作普通的字符来进行匹配。例如正则表达式\$被用来匹配美元符号,而不是行尾,类似的,正则表达式\.用来匹配点字符,而不是任何字符的通配符。 |
[ ] |
匹配括号中的任何一个字符。例如正则表达式r[aou]t匹配rat、rot和rut,但是不匹配ret。可以在括号中使用连字符-来指定字符的区间,例如正则表达式[0-9]可以匹配任何数字字符;还可以制定多个区间,例如正则表达式[A-Za-z]可以匹配任何大小写字母。另一个重要的用法是“排除”,要想匹配除了指定区间之外的字符——也就是所谓的补集——在左边的括号和第一个字符之间使用^字符,例如正则表达式[^269A-Z] 将匹配除了2、6、9和所有大写字母之外的任何字符。 |
\< \> |
匹配词(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+匹配9、99、999等。注意:这个元字符不是所有的软件都支持的。 |
? |
匹配0或1个正好在它之前的那个字符。注意:这个元字符不是所有的软件都支持的。 |
{i} |
匹配指定数目的字符,这些字符是在它之前的表达式定义的。例如正则表达式A[0-9]{3} 能够匹配字符"A"后面跟着正好3个数字字符的串,例如A123、A348等,但是不匹配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-
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()方法。