• 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()方法。
    

  • 相关阅读:
    谈谈适配器模式
    最近面试的感想
    如何解决超链接访问后hover样式就不渲染
    单页面和多页面的网页差别比较(SPA)
    mongoDB发生服务特定错误: 100.
    win系统没有此电脑怎么办?
    u盘空间变小,少了好多空间
    VS Code 解决 因为在此系统上禁止运行脚本
    远离麻木的感觉
    瀑布布局(waterflall flow)实现
  • 原文地址:https://www.cnblogs.com/sharpxiajun/p/2184636.html
Copyright © 2020-2023  润新知