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标志实例分析”有了更深的了解,不妨来实际操作一番吧!这里是恰卡编程网网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!
推荐阅读
-
JavaScript闭包用多会造成内存泄露吗
-
javascript中文乱码如何解决
-
PHP学习第十五天——JavaScript入门DOM对象:二
-
Node.js基本内容和知识点
简单的说node.js就是运行在服务端的JavaScript,起初段定位是后端开发语言,由于技术的不够成熟,一般小型项目...
-
PHP与Node.js:一个史诗般开发者的分享
-
JavaScript 中 find() 和 filter() 方法的区别
JavaScript在ES6上有很多数组方法,每种方法都有独特的用途和好处。在开发应用程序时,大多使用数组方法来获...
-
js怎么跟php结合使用
-
简单说说Node.js和JavaScript
Node.js是一个开源和跨平台的JavaScript运行时环境,在浏览器之外运行V8JavaScript引擎(...
-
前端开发工程师专业技能简历范文
-
JavaScript怎么实现淘宝网图片的局部放大功能