深入了解javascript中的exec与match方法

2024-06-15 08:32

本文主要是介绍深入了解javascript中的exec与match方法,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

这是我在csdn上的一篇文章,今天又忘记match的特性了,发现知识还得温故而知新啊。

      

   一直以来对exec和match方法的区别有些混淆,今天重看帮助文档(帮助文档中主要依据一些晦涩的例子佐证,无助于理解),然后在百度搜集了下一些介绍文章,其中下面一篇文章(楼兰之风...的《彻底领悟javascript中的exec与match方法》)被多次检索,并在百度搜索第一条。但是看完后,做了些例子,发现作者的结论有些问题,更正下,以避免误导各位筒子们。

1.微笑在看文章之前之前,引用下这个经典面试题,如果知晓一下题目的答案,那就没有必要往下看了。

var someText="web2.0 .net2.0";
var pattern=/(\w+)(\d)\.(\d)/g;
var outCome_exec=pattern.exec(someText);
var outCome_matc=someText.match(pattern);

What is outCome_exec[1] and outCome_matc[1]? 

Choice A: true 
Choice B: false 
Choice C: null 
Choice D: Web 
Choice E: Web2.0 
Choice F: undefined
Choice G: net2.0

 

思考1分钟ing........

 

 

 

 

 

 

            有些知识没用过,思考两天也没用,所以思考不用太久。碰见这样问题就直接google和百度吧!
2.思考未果完毕,现在看原文以及文中的结论:

作者原文如下:

 http://www.cnblogs.com/xiehuiqi220/archive/2008/11/05/1327487.html

 

最后作者总结的结论如下:

a)match是返回所有匹配的字符串合成的数组,但是正则表达式必须指定全局g属性才能返回所有匹配,不指定g属性则会返回一个只有一个元素的数组。

b)exec永远返回与第一个匹配相关的信息,其返回数组包括第一个匹配的字串,所有分组的反向引用。

3.发现问题:

3-1)以上结论是错误的。在使用match方法时,如果不指定g属性,则与RegExp对象的exec方法可以等价,而不是只有一个元素的数组。

举例:

var str= "ahi" ;
var exp=/a(hi)/;

var arr1 = exp.exec(str);
var arr2 = str.match(exp);
alert(arr1);//结果:arr1.length==2;arr1[0]==ahi;arr1[1]==hi;
alert(arr2);//结果:arr2.length==2;arr2[0]==ahi;arr1[1]==hi;结果同上

 

 

3-2)同时,在js帮助文档中,在执行exec方法时,如果有属性g,将该对象的匹配的开始位置设置到紧接这匹配子串的字符位置,当第二次调用exec时,将从
lastIndex所指示的字符位置开始检索。利用这个特点可以反复调用exec遍历所有匹配,此时等价于match具有g属性的情况(其实就是将匹配的结果放入Matches 集合中去了)。

举例如下:

a)有属性g的情况时,更新了index和lastIndex,对下次检索起到作用:

function RegExpTest() {
    var src = "The rain in Spain falls mainly in the plain.";
    var re = /(\w+)/g; // 创建正则表达式模式。    
    var arr;
    while ((arr = re.exec(src)) != null){
     document.write(arr.index + "-" + RegExp.lastIndex + "\t" + arr[0]);//此处RegExp.lastIndex和arr.lastIndex均有同样的属性,可以互换。在此注意IE6和7的lastIndex重设置0的bug

    }
};

RegExpTest();

//以上例子可以遍历所匹配的内容。并可得到每个小匹配的index和lastIndex;

b)如果以上例子没有g的情况,则以上例子,exec方法没有更新RegExp 对象的全局属性(index、lastIndex等),以上例子会陷入死循环,index和lastIndex一直为0和3

 

可见属性g在exec过程中可以改变index和lastIndex等的值,以便下一次检索的位置,match方法无此能力。

 

4.关于index和lastIndex等属性(帮助中还有leftContext、rightContext、lastMatch、lastParen(最后一个括号),但是这些属性均以index和lastindex为基础)。

4-1)只读属性。

如下例子:

    var src = "The rain in Spain falls mainly in the plain.";
    var re = /(\w+)/g; // 创建正则表达式模式。   
    var arr;
    arr = re.exec(src);    
    RegExp.lastIndex = 0;
    RegExp.index = 0;
    arr.lastIndex = 0;
    arr.index = 0;

    document.write(arr.index + "-" + arr.lastIndex + "\t" + arr[0]+"**********"+RegExp.index + "-" + RegExp.lastIndex + "\t" + arr[0]);

    //结果为0-0 The**********0-3 The。

究其原因也就是RegExp的属性是只读的,即使js语言的灵活性,可以修任何属性或添加任何属性,均不报语法错误。但是依旧无法RegExp的属性更改,但是arrary对象则是可以更改,但是每次执行一次exec,就会将RegExp.index等属性重新赋值给返回的Arrary对象。

例如:

var src = "The rain in Spain falls mainly in the plain.";
var re = /(\w+)/g; // 创建正则表达式模式。 
var arr;
arr = re.exec(src); 
RegExp.lastIndex = 0;
RegExp.index = 0;
arr.lastIndex = 0;
arr.index = 0;

document.write(arr.index + "-" + arr.lastIndex + "\t" + arr[0]+"**********"+RegExp.index + "-" + RegExp.lastIndex + "\t" + arr[0]);

//执行第二次arr的index属性会被更新,其实是RegExp对象实例在执行exec方法时,更新全局的RegExp.index和arr的index等,在后边会介绍

arr = re.exec(src);
document.write("<br/>"+arr.index + "-" + arr.lastIndex + "\t" + arr[0]+"**********"+RegExp.index + "-" + RegExp.lastIndex + "\t" + arr[0]);

//0-0 The**********0-3 The
//4-8 rain**********4-8 rain

4-2)不同的RegExp实例对象交叉执行exec时,index、lastIndex等属性互不影响。每次执行exec或者执行String的match方法时,都会给RexExp.index等赋予新值。(这个其实是必须的,只是我在这脑袋一犯浑,给理解错了,主要是因为“RegExp.lastIndex = 0;”可以被赋值,但是取值时,结果又没有改变,让我脑袋混乱了。)

 

开始我以为如果两个RegExp对象在交叉执行exec时,可能index等会清零。因为我认为index属性是保存在RegExp的全局静态属性上的。现在发现是保存在具体的RegExp实例上,每次执行exec或者执行String的match方法时,都会给RexExp.index等赋予新值。

呵呵,这可能是习惯了c和java中类和类实例的想法的人常犯的错误,认为RegExp是个类,RegExp.index是一个类的static属性。这样认为没错,但是他的值是是会在执行exec和String的match方法时,被正则对象更新。

举例如下:

    var src = "The rain in Spain falls mainly in the plain.";
       
    var re1 = /(\w+)/; // 创建正则表达式模式。 
    var re2 = /(\w+)/g; // 创建正则表达式模式。 
    var arr;

    arr = re1.exec(src);    
    document.write("R1第一次执行exec:"+RegExp.index + "-" + RegExp.lastIndex + "\t" + arr[0]);
    
    arr = re2.exec(src);
    document.write("<br/>R2第一次执行exec:"+RegExp.index + "-" + RegExp.lastIndex + "\t" + arr[0]);
    
    arr = re1.exec(src);
    document.write("<br/>R1第二次执行exec:"+RegExp.index + "-" + RegExp.lastIndex + "\t" + arr[0]);

    arr = re2.exec(src);
    document.write("<br/>R2第二次执行exec:"+RegExp.index + "-" + RegExp.lastIndex + "\t" + arr[0]);

输出的结果如下:

R1第一次执行exec:0-3 The
R2第一次执行exec:0-3 The
R1第二次执行exec:0-3 The
R2第二次执行exec:4-8 rain

 

4-3)String对象的match方法,无法像exec方法那样获取中间查找的对象的index和lastIndex,也就是说是一次性的。即无法得到下一次检索的位置,match方法在设置g属性时,只能获取最后一个检索和index和lastIndex;match在没有设置g属性时,仅仅获得第一个匹配的index和lastIndex。

举例如下:

a)

var src = "The rain in Spain falls mainly in the plain.";
var re = /\w+/g; //有g属性。 
var i = 0;
while (i++<10){
    arr = src.match(re);
    document.write(RegExp.index + "-" + RegExp.lastIndex + "\t" + arr + "<br/>");

}


//结果如下:

38-43 The,rain,in,Spain,falls,mainly,in,the,plain
38-43 The,rain,in,Spain,falls,mainly,in,the,plain
38-43 The,rain,in,Spain,falls,mainly,in,the,plain
38-43 The,rain,in,Spain,falls,mainly,in,the,plain
38-43 The,rain,in,Spain,falls,mainly,in,the,plain
38-43 The,rain,in,Spain,falls,mainly,in,the,plain
38-43 The,rain,in,Spain,falls,mainly,in,the,plain
38-43 The,rain,in,Spain,falls,mainly,in,the,plain
38-43 The,rain,in,Spain,falls,mainly,in,the,plain
38-43 The,rain,in,Spain,falls,mainly,in,the,plain

 

b)

var src = "The rain in Spain falls mainly in the plain.";
var re = /\w+/; // 无g属性。 
var i = 0;
while (i++<10){
    arr = src.match(re);
    document.write(RegExp.index + "-" + RegExp.lastIndex + "\t" + arr + "<br/>");

}
//结果如下:

0-3 The
0-3 The
0-3 The
0-3 The
0-3 The
0-3 The
0-3 The
0-3 The
0-3 The
0-3 The

c)

var src = "The rain in Spain falls mainly in the plain.";
var re = /\w+/g; 
var i = 0;
arr = src.match(re);
while (arr[i]!=null){
    document.write(RegExp.index + "-" + RegExp.lastIndex + "\t" + arr[i] + "<br/>");
    i++;

//结果如下:

38-43 The
38-43 rain
38-43 in
38-43 Spain
38-43 falls
38-43 mainly
38-43 in
38-43 the
38-43 plain

5.最后结论(如有不对,请指正):

1)exec是RegExp对象方法,match是String对象方法;

2)如果没有找到结果,则二者都返回null;

3)只有在正则表达式必须指定全局g属性时,match才能返回所有匹配,否则match与exec方法结果无差异,是等价的;

4)exec永远返回与第一个匹配相关的信息,其返回数组第一个值是第一个匹配的字串,剩下的是所有分组的反向引用(即子括号的匹配内容);

5)exec在设置g属性后,虽然匹配结果不受g的影响,返回结果仍然是一个数组(第一个值是第一个匹配到的字符串,以后的为分组匹配内容),但是会改变index和lastIndex等的值,将该对象的匹配的开始位置设置到紧接这匹配子串的字符位置,当第二次调用exec时,将从lastIndex所指示的字符位置开始检索。同样match方法在设置了g属性后,也会改变index和lastIndex的值,但是是一次性的。无法像exec那样能逐过程累积(即将结果放入Matches 集合中去了),因此无法累积获取下一次检索的位置。

 

PS:

        最开始那个问题的答案为D和G。你想明白了么?

        以上测试均在ie和firefox中测试过,结果一致。

        以上测试的前提是javascript支持RegExp对象。早期浏览器的javascript引擎未必支持正则对象或者未必支持正则表达式对象的某些属性。

这篇关于深入了解javascript中的exec与match方法的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1062942

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

关于数据埋点,你需要了解这些基本知识

产品汪每天都在和数据打交道,你知道数据来自哪里吗? 移动app端内的用户行为数据大多来自埋点,了解一些埋点知识,能和数据分析师、技术侃大山,参与到前期的数据采集,更重要是让最终的埋点数据能为我所用,否则可怜巴巴等上几个月是常有的事。   埋点类型 根据埋点方式,可以区分为: 手动埋点半自动埋点全自动埋点 秉承“任何事物都有两面性”的道理:自动程度高的,能解决通用统计,便于统一化管理,但个性化定

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory