JavaScript正则表达式中g标志实例分析

JavaScript正则表达式中g标志实例分析

本篇内容主要讲解“JavaScript正则表达式中g标志实例分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“JavaScript正则表达式中g标志实例分析”吧!

    有一天在思否社区看到有个问题,大致描述如下

    constlist=['a','b','-','c','d'];constreg=/[a-z]/g;constletters=list.filter(i=>reg.test(i));//letters===['a','c'];//如果正则不使用`g`标志可以得到所有的字母//为什么加入`g`之后就不可以了

    对问题而言,遍历中的i就是一个字符,不需要用到g。

    但是就我对正则的理解(过于浅薄)感觉上有没有g(只是全局搜索,不会匹配到就停下来)应该不影响,激发了我的好奇心。

    上面题的建议写法如下

    constreg=/[a-z]/g;reg.test('a');//=>truereg.test('a');//=>falsereg.test('a');//=>truereg.test('a');//=>falsereg.test('a');//=>true

    解密过程

    首先可以确定的表现一定是g导致的

    搜索引擎

    打开 MDN 仔细查看g标志的作用,得到结论和我的理解无二。

    我猜想应该就是g可能启用了某种缓存,又因为reg相对过滤器是全局变量,我将代码改为:

    constlist=['a','b','-','c','d'];constletters=list.filter(i=>/[a-z]/g.test(i));//letters===['a','b','c','d'];

    将正则声明到每一次遍历,得到结论就是正确的,验证了我的猜想。也得到了,缓存就是正则中的某个地方

    下面我找到对应的源码来查看问题的原因

    源码层面

    由于最近在看 Rust,所以使用 Rust 编写的源码查看

    打开项目后,点击.进入 vscode 模式,command+p 搜索 regexp 关键词

    进入test.rs文件,command+f 搜索/g可以找到在 90 行有个last_index()的测试

    #[test]fnlast_index(){letmutcontext=Context::default();letinit=r#"varregex=/[0-9]+(\.[0-9]+)?/g;"#;//forward的作用:更改context,并返回结果的字符串。eprintln!("{}",forward(&mutcontext,init));assert_eq!(forward(&mutcontext,"regex.lastIndex"),"0");assert_eq!(forward(&mutcontext,"regex.test('1.0foo')"),"true");assert_eq!(forward(&mutcontext,"regex.lastIndex"),"3");assert_eq!(forward(&mutcontext,"regex.test('1.0foo')"),"false");assert_eq!(forward(&mutcontext,"regex.lastIndex"),"0");}

    看到了有lastIndex关键字,这里再已经大致猜到问题的原因了,g 标志存在匹配后的最后一个下标,导致出现问题。

    我们将视线移入到mod.rs文件中,搜索test

    在 631 行看到了fn test()方法

    pub(crate)fntest(this:&JsValue,args:&[JsValue],context:&mutContext,)->JsResult<JsValue>{//1.LetRbethethisvalue.//2.IfType(R)isnotObject,throwaTypeErrorexception.letthis=this.as_object().ok_or_else(||{context.construct_type_error("RegExp.prototype.testmethodcalledonincompatiblevalue")})?;//3.Letstringbe?ToString(S).letarg_str=args.get(0).cloned().unwrap_or_default().to_string(context)?;//4.Letmatchbe?RegExpExec(R,string).letm=Self::abstract_exec(this,arg_str,context)?;//5.Ifmatchisnotnull,returntrue;elsereturnfalse.ifm.is_some(){Ok(JsValue::new(true))}else{Ok(JsValue::new(false))}}

    test()方法中找到了Self::abstract_exec()方法

    pub(crate)fnabstract_exec(this:&JsObject,input:JsString,context:&mutContext,)->JsResult<Option<JsObject>>{//1.Assert:Type(R)isObject.//2.Assert:Type(S)isString.//3.Letexecbe?Get(R,"exec").letexec=this.get("exec",context)?;//4.IfIsCallable(exec)istrue,thenifletSome(exec)=exec.as_callable(){//a.Letresultbe?Call(exec,R,«S»).letresult=exec.call(&this.clone().into(),&[input.into()],context)?;//b.IfType(result)isneitherObjectnorNull,throwaTypeErrorexception.if!result.is_object()&&!result.is_null(){returncontext.throw_type_error("regexpexecreturnedneitherobjectnornull");}//c.Returnresult.returnOk(result.as_object().cloned());}//5.Perform?RequireInternalSlot(R,[[RegExpMatcher]]).if!this.is_regexp(){returncontext.throw_type_error("RegExpExeccalledwithinvalidvalue");}//6.Return?RegExpBuiltinExec(R,S).Self::abstract_builtin_exec(this,&input,context)}

    又在Self::abstract_exec()方法中找到了Self::abstract_builtin_exec()方法

    pub(crate)fnabstract_builtin_exec(this:&JsObject,input:&JsString,context:&mutContext,)->JsResult<Option<JsObject>>{//1.Assert:RisaninitializedRegExpinstance.letrx={letobj=this.borrow();ifletSome(rx)=obj.as_regexp(){rx.clone()}else{returncontext.throw_type_error("RegExpBuiltinExeccalledwithinvalidvalue");}};//2.Assert:Type(S)isString.//3.LetlengthbethenumberofcodeunitsinS.letlength=input.encode_utf16().count();//4.LetlastIndexbeℝ(?ToLength(?Get(R,"lastIndex"))).letmutlast_index=this.get("lastIndex",context)?.to_length(context)?;//5.LetflagsbeR.[[OriginalFlags]].letflags=&rx.original_flags;//6.Ifflagscontains"g",letglobalbetrue;elseletglobalbefalse.letglobal=flags.contains('g');//7.Ifflagscontains"y",letstickybetrue;elseletstickybefalse.letsticky=flags.contains('y');//8.Ifglobalisfalseandstickyisfalse,setlastIndexto0.if!global&&!sticky{last_index=0;}//9.LetmatcherbeR.[[RegExpMatcher]].letmatcher=&rx.matcher;//10.Ifflagscontains"u",letfullUnicodebetrue;elseletfullUnicodebefalse.letunicode=flags.contains('u');//11.LetmatchSucceededbefalse.//12.Repeat,whilematchSucceededisfalse,letmatch_value=loop{//a.IflastIndex>length,theniflast_index>length{//i.Ifglobalistrueorstickyistrue,thenifglobal||sticky{//1.Perform?Set(R,"lastIndex",+0????,true).this.set("lastIndex",0,true,context)?;}//ii.Returnnull.returnOk(None);}//b.Letrbematcher(S,lastIndex).//Checkiflast_indexisavalidutf8indexintoinput.letlast_byte_index=matchString::from_utf16(&input.encode_utf16().take(last_index).collect::<Vec<u16>>(),){Ok(s)=>s.len(),Err(_)=>{returncontext.throw_type_error("Failedtogetbyteindexfromutf16encodedstring")}};letr=matcher.find_from(input,last_byte_index).next();matchr{//c.Ifrisfailure,thenNone=>{//i.Ifstickyistrue,thenifsticky{//1.Perform?Set(R,"lastIndex",+0????,true).this.set("lastIndex",0,true,context)?;//2.Returnnull.returnOk(None);}//ii.SetlastIndextoAdvanceStringIndex(S,lastIndex,fullUnicode).last_index=advance_string_index(input,last_index,unicode);}Some(m)=>{//c.Ifrisfailure,then#[allow(clippy::if_not_else)]ifm.start()!=last_index{//i.Ifstickyistrue,thenifsticky{//1.Perform?Set(R,"lastIndex",+0????,true).this.set("lastIndex",0,true,context)?;//2.Returnnull.returnOk(None);}//ii.SetlastIndextoAdvanceStringIndex(S,lastIndex,fullUnicode).last_index=advance_string_index(input,last_index,unicode);//d.Else,}else{//i.Assert:risaState.//ii.SetmatchSucceededtotrue.breakm;}}}};//13.Leteber'sendIndexvalue.letmute=match_value.end();//14.IffullUnicodeistrue,thenifunicode{//eisanindexintotheInputcharacterlist,derivedfromS,matchedbymatcher.//LeteUTFbethesmallestindexintoSthatcorrespondstothecharacteratelementeofInput.//IfeisgreaterthanorequaltothenumberofelementsinInput,theneUTFisthenumberofcodeunitsinS.//b.SetetoeUTF.e=input.split_at(e).0.encode_utf16().count();}//15.Ifglobalistrueorstickyistrue,thenifglobal||sticky{//a.Perform?Set(R,"lastIndex",????(e),true).this.set("lastIndex",e,true,context)?;}//16.Letnbethenumberofelementsinr'scapturesList.(Thisisthesamevalueas22.2.2.1'sNcapturingParens.)letn=match_value.captures.len();//17.Assert:n<23^2-1.debug_assert!(n<23usize.pow(2)-1);//18.LetAbe!ArrayCreate(n+1).//19.Assert:ThemathematicalvalueofA's"length"propertyisn+1.leta=Array::array_create(n+1,None,context)?;//20.Perform!CreateDataPropertyOrThrow(A,"index",????(lastIndex)).a.create_data_property_or_throw("index",match_value.start(),context).expect("thisCreateDataPropertyOrThrowcallmustnotfail");//21.Perform!CreateDataPropertyOrThrow(A,"input",S).a.create_data_property_or_throw("input",input.clone(),context).expect("thisCreateDataPropertyOrThrowcallmustnotfail");//22.LetmatchedSubstrbethesubstringofSfromlastIndextoe.letmatched_substr=ifletSome(s)=input.get(match_value.range()){s}else{""};//23.Perform!CreateDataPropertyOrThrow(A,"0",matchedSubstr).a.create_data_property_or_throw(0,matched_substr,context).expect("thisCreateDataPropertyOrThrowcallmustnotfail");//24.IfRcontainsanyGroupName,then//25.Else,letnamed_groups=match_value.named_groups();letgroups=ifnamed_groups.clone().count()>0{//a.Letgroupsbe!OrdinaryObjectCreate(null).letgroups=JsValue::from(JsObject::empty());//Perform27.fhere//f.IftheithcaptureofRwasdefinedwithaGroupName,then//i.LetsbetheCapturingGroupNameofthecorrespondingRegExpIdentifierName.//ii.Perform!CreateDataPropertyOrThrow(groups,s,capturedValue).for(name,range)innamed_groups{ifletSome(range)=range{letvalue=ifletSome(s)=input.get(range.clone()){s}else{""};groups.to_object(context)?.create_data_property_or_throw(name,value,context).expect("thisCreateDataPropertyOrThrowcallmustnotfail");}}groups}else{//a.Letgroupsbeundefined.JsValue::undefined()};//26.Perform!CreateDataPropertyOrThrow(A,"groups",groups).a.create_data_property_or_throw("groups",groups,context).expect("thisCreateDataPropertyOrThrowcallmustnotfail");//27.Foreachintegerisuchthati≥1andi≤n,inascendingorder,doforiin1..=n{//a.LetcaptureIbeithelementofr'scapturesList.letcapture=match_value.group(i);letcaptured_value=matchcapture{//b.IfcaptureIisundefined,letcapturedValuebeundefined.None=>JsValue::undefined(),//c.ElseiffullUnicodeistrue,then//d.Else,Some(range)=>{ifletSome(s)=input.get(range){s.into()}else{"".into()}}};//e.Perform!CreateDataPropertyOrThrow(A,!ToString(????(i)),capturedValue).a.create_data_property_or_throw(i,captured_value,context).expect("thisCreateDataPropertyOrThrowcallmustnotfail");}//28.ReturnA.Ok(Some(a))}

    Self::abstract_builtin_exec()方法中存在global以及last_index这样看来最终执行的方法就是在这里了,仔细查看该方法中的代码(代码写的很详细而且每一步都有注释)

    在第 12 步中:

    • lastIndex 超过文本长度且当 global 存在时将 lastIndex 置为 0

    • 获取匹配到的值(match_value

      如果未匹配到则置为advance_string_index()方法的返回值

      advance_string_index()不在当前问题的考虑范围https://tc39.es/ecma262/#sec-...

    第 13 步获取匹配到的值的 endIndex

    第 15 步将 lastIndex 置为 endIndex

    至此也就整明白了g标志的含义,在正则的原型链中存在一个lastIndex,如果匹配为真时lastIndex不会重置为 0 ,下一次开始时继承了上次位置,

    结论

    在问题代码中分析

    constreg=/[a-z]/g;//声明后,lastIndex为0reg.test('a');//=>true;第一次匹配后,lastIndex为1reg.test('a');//=>false;第二次匹配由于lastIndex为1,且字符只有一个,得到false,将lastIndex置为0reg.test('a');//=>true;下面依次循环前两次的逻辑reg.test('a');//=>false;reg.test('a');//=>true;

    到此,相信大家对“JavaScript正则表达式中g标志实例分析”有了更深的了解,不妨来实际操作一番吧!这里是恰卡编程网网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

    发布于 2022-03-18 22:49:59
    收藏
    分享
    海报
    0 条评论
    21
    上一篇:Springboot中静态文件的引入方式有哪些 下一篇:Java数组、字符和等差数列问题怎么解决
    目录

      0 条评论

      本站已关闭游客评论,请登录或者注册后再评论吧~

      忘记密码?

      图形验证码