本文发布在我的博客一道小小的题目引发对javascript支持正则表达式相关方法的探讨
许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。
以前对于正则是非常惧怕的,因为看不懂和学不会。但最近项目中频繁的使用到了正则,因此强迫自己去学习了解,慢慢的体会到了他的魅力与强大。当然学习正则初入门的时候有些枯燥难懂,但越学越觉得轻松。本文不准备说关于正则本身的事儿,而是说一说关于javascript中关于正则的几个方法中被很多人忽略的地方。
这道题目是在群里日常闲聊时,公司同事抛出来的,具体是出自哪里本人没去考察。先先说说题目:
写一个方法把一个数字末尾的连续
变成
- 0
,如
- 9
变成
- 1230000
- 1239999
一道很简单的题目,直接正则就能搞定,也许你会写:
- function zoreToNine(num) {
- return (num + '').replace(/0/g, 9);
- }
- //或者
- function zoreToNine(num) {
- return (num + '').replace(/[1-9]0+$/, 9);
- }
这也是此题的陷阱所在,按照上面的方法,
就会被转化成
- 1023000
,这样是不符合要求的,所以改进一下:
- 1923999
- function zoreToNine(num){
- return (num + '').replace(/[1-9]0+$/,function($1){
- return $1.replace(/0/g,9);
- });
- }
- zoreToNine(1223000); //1223999
- zoreToNine(1023000); //1023999
上述方法使用了正则,有趣的是在回调函数里有一个
,这个
- $1
到底是什么?所有的匹配规则匹配后都有
- $1
这个变量么?...一连串的问题,以前我从来没有去追探过,趁着昨个比较空闲,去追探了一番,并在今天整理了一下,写下此文记录。
- $1
当然方法很多,不一定非要用正则,还完全可以使用纯算术的方法实现,大家有兴趣可以尝试,闲话少说进入这次的主题:
支持正则表达式相关方法,注意并不是正则对象的方法。
- javascript
中正则对象有三个方法:
- javascript
、
- test
和
- exec
,但是此次的主角并不是它们!主角是能够使用正则表示的相关方法:
- compile
、
- search
、
- match
和
- replace
,注意它们都是
- split
对象的方法,使用它们必须要是
- String
类型.
- String
是一个用于替换字符串的方法,虽然看似简单,但是它隐藏的机关也是常常被人忽略。具体分析一下它的特点: 它接收两个参数 无副作用不影响原始变量 返回被改变的字符串(一定是字符串类型)
- replace
定义一些变量,方便全文取用。
- let a = '12309800', b = '12309800[object Object]', b = '12309800{}';
在一般情况,
参数一般是正则、字符串、数字。如果是字符串,将会在匹配到第一个符合条件的目标,结束方法;如果是正则,则按照正则的规则进行匹配
- rule
- //匹配第一个0替换成5
- a.replace(0,5); //'12359800'
- //匹配所有的0替换成5
- a.replace(/0/g,5); //'12359855'
在一般情况,
参数是字符串、数字、者回调。
- replacement
当参数
为正则,并且正则至少包含有一对完整的
- rule
时,如果
- ()
包含有$的字符串,那么对于
- replacement
(n为大于0的整数,n的长度取决于正则中括号的对数),会被解析成一个变量。但是无法字符串中进行计算,此时更类似特别的字符串模板变量。
- $n
一般情况下,
中
- $n
的长度取决于正则中括号的对数,
- n
表示第1对括号匹配的结果,
- $1
表示第2对匹配的结果...在正则所有的括号对中,左括号出现在第几个位置(或者说从左往右),则它就是第几对括号,以此类推。假设我们把这种规则成为
- $2
(ps:这完全是我自己取的一个名字,方便文章后面使用和记忆)。
- 正则匹配分割规则
- a.replace(0,'$0'); //'123$09800'
- a.replace(/00/g,'$0'); //'123098$0'
- a.replace(/[1-9]0+$/,'$1'); //'12309$1'
- a.replace(/([1-9](0+$))/,'$1'); //'12309800',此时$1为[1-9](0+$)匹配到的内容,$2为0+$匹配到的内容
- a.replace(/([1-9])(0+$)/,'$1'); //'123098',此时$1为[1-9]匹配到的内容,$2为0+$匹配到的内容
- a.replace(/([1-9])(0+$)/,'$1*$2'); //'123098*00',此处的$1和$2不会安照期待的情况进行乘法计算,要进行计算可以用回调
如果正则中包含有全局匹配标志(g/gi),那么每次匹配的都符合上述规则
先看例子:
- a.replace(/[1-9]0+$/,function(){
- console.log(arguments); //["800",5,"12309800"]
- });
- a.replace(/([1-9])0+$/,function(){
- console.log(arguments); //["800","8",5,"12309800"]
- });
- a.replace(/([1-9])(0+$)/,function(){
- console.log(arguments); //["800","8","00",5,"12309800"]
- });
- a.replace(/(([1-9])(0+$))/,function(){
- console.log(arguments); //["800","800","8","00",5,"12309800"]
- });
回调函数的
数组部分组成:[完整匹配的字符串,$1,$2,...,$n,匹配的开始位置,原始字符串],
- arguments
表示每个括号对的匹配,规则和前面的相同,所以有一下规律:注意:除了匹配的开始位置是
- $1...$n
类型外,其余的都是
- Number
类型
- String
- let arr = [...arguments],
- len = arr.length; (len >= 3) === true;
- arr[0] = 完整匹配的字符串;
- arr[len - 1] = 原始字符串;
- arr[len - 2] = 匹配的开始位置;
如果参数类型不是上述两种情况,会发生什么呢?看看下面的例子:
- a.replace(0,null); //123null9800
- a.replace(0,undefined); //123null9800
- a.replace(0,[]); //1239800
- a.replace(0,Array); //1230,3,123098009800
- b.replace({},5); //123098005
- c.replace({},5); //'12309800{}'
- a.replace(0,{}); //123[object Object]9800
- a.replace(0,Object); //12309800
由上面的例子可以看出,如果非正则也非字符串,则有以下规则:
变量,则会转换成
- null
字符串;
- 'null'
变量,则会转换成
- undefined
字符串;
- 'undefined'
变量,则会调用join()方法转换成字符串,默认以
- []
分割,值得注意的是空数组将会被转换成空字符串(没有任何字符),通常会被匹配源字符串的开始位置(默认开始位置为空字符串);
- ,
变量,则会先转成成一个匹配的数组,形如
- Array
,然后对它调用
- [完整匹配的字符串,$1,$2,...,$n,匹配的开始位置,原始字符串]
方法转换成字符串,默认以
- join()
分割;
- ,
变量,则会调用
- {}
方法把
- Object.protype.toString.call()
转换成
- {}
;
- [object Object]
变量,则貌似什么都没做
- Object
虽然可以传入这些非正常参数,但大多数情况下这些类型的参数对实际是毫无意义的,所以不建议传入以上类型的参数。同上面的
一样,为了方便使用称呼,姑且我把上面的转换规则称为
- 正则匹配分割规则
- 正则匹配参数转换规则
方法可在字符串内检索指定的值,或找到一个或多个正则表达式的匹配。该方法类似
- match
和
- indexOf
,但是它返回指定的值,而不是字符串的位置;
- lastIndexOf
参数的传递除了常规的正则和字符串以外,其余所有类型的参数都会按照上述的
转换成字符串形式来匹配。
- 正则匹配参数转换规则
返回值根据传入的参数类型和规则的不同,返回的内容不同,但总体来说,它是返回一个对象,而不是索引,如果没匹配到任何符合条件的字符串,则返回
。
- null
如果匹配规则是一个非全局匹配规则,那么,它此时的返回值是一个伪数组对象(likeArr),形如:[一个展开的匹配到的字符串数组, 匹配到的字符串位置, 原始字符串],它有如下规律:
- var likeArr = a.match(regex);
- likeArr[0] = 匹配到的字符串;
- likeArr[1...n] = 正则匹配分割规则匹配的字符串;
- likeArr.index = 匹配到字符串的位置
- likeArr.inupt = 原始字符串
看例子:
- a.match(/[1-9]0+$/); //[0:'800',index:5,input:'12309800']
- a.match(/([1-9])0+$/); //[0:'800',1:'8',index:5,input:'12309800']
- a.match(/[1-9](0+$)/); //[0:'800',1:'00',index:5,input:'12309800']
- a.match(/([1-9])(0+$)/); //[0:'800',1:'8',2:'00',index:5,input:'12309800']
如果匹配规则是一个非全局匹配规则,那么,它此时的返回值是一个数组对象(arr),形如:[匹配到的字符串数1,匹配到的字符串数2,匹配到的字符串数3];
看例子:
- a.match(/[1-9]0/); //[0:'30',index:2,input:'12309800']
- a.match(/[1-9]0/g); //[0:'30',1:'80']
方法用于检索字符串中指定的子字符串,或检索与正则表达式相匹配的子字符串。
- search
中第一个与
- stringObject
相匹配的子串的起始位置。如果没有找到任何匹配的子串,则返回
- rule
。注意:
- -1
方法不执行全局匹配,它将忽略标志
- search
。
- g/gi
的
- regexp
属性,总是从字符串的开始进行检索,这意味着它总是返回
- lastIndex
的第一个匹配的位置
- stringObject
同样,
可以传入任何参数类型,它会遵循
- search
进行转换
- 正则匹配参数转换规则
这个方法就不用多说,很常用的字符串分割方法。
第二个参数的作用就是限制返回值的长度,表示返回值的最大长度
当然,它依然可以传入任何参数类型,会遵循
进行转换
- 正则匹配参数转换规则
写了这么多,突然发现以前仅仅是在用这些方法,了解得很不够深入。越是学习才发现其中的奥秘!学无止境,与诸君共勉!
以上内容如有错误之处,希望诸君不吝指出!
来源: https://juejin.im/post/5a17cec06fb9a0451238a1ff