资讯专栏INFORMATION COLUMN

JavaScript正则表达式学习笔记(二) - 打怪升级

Jioby / 1978人阅读

摘要:本文接上篇,基础部分相对薄弱的同学请移步正则表达式学习笔记一理论基础。正则表达式标志符全局匹配,即找到所有匹配的。方法返回结果的格式不一致问题这个问题上文正则表达式学习笔记一理论基础也有体现,这里再多带带拿来说一说,以加深记忆。

本文接上篇,基础部分相对薄弱的同学请移步《JavaScript正则表达式学习笔记(一) - 理论基础》。上文介绍了8种JavaScript正则表达式的属性,本文还会追加介绍几种JavaScript正则表达式的属性(注意是非标准属性,但很好用)。
一. 上文回顾
本文会用到上篇文章部分内容,所以简单回顾下。
1.1 JavaScript正则表达式标志符

g: 全局匹配,即找到所有匹配的。对应属性RegExp#global

i: 忽略字母大小写。对应属性RegExp#ingoreCase

m: 多行匹配,只影响^和$,二者变成行的概念,即行开头和行结尾。对应属性RegExp#multiline

u: ES6新增。含义为“Unicode 模式”,用来正确处理大于uFFFF的 Unicode 字符。也就是说,会正确处理四个字节的 UTF-16 编码。对应属性RegExp#unicode

y: ES6新增。y修饰符的作用与g修饰符类似,也是全局匹配,后一次匹配都从上一次匹配成功的下一个位置开始。不同之处在于,g修饰符只要剩余位置中存在匹配就可,而y修饰符确保匹配必须从剩余的第一个位置开始,这也就是“粘连”的涵义。对应属性RegExp#sticky

1.2 适用于Javascript正则表达式的方法

上篇文章《JavaScript正则表达式学习笔记(一) - 理论基础》介绍了适用于JavaScript正则表达式模式匹配的相关API共有6种,RexExp提供2个,String提供4个,如下:

1. RegExp#test    // 适用于:验证、提取
2. RegExp#exec    // 适用于:验证、提取
3. String#search  // 适用于:验证、提取
4. String#match   // 适用于:验证、提取
5. String#split   // 适用于:切分
6. String#replace // 适用于:提取、替换
二. JavaScript正则表达式的四种操作

正则表达式是用于匹配字符串中字符组合的模式, 其核心内容便是模式匹配。也就是说,不论进行那种操作,首先要有模式匹配,有了模式匹配之后,才能进行验证、替换、切分、提取这四种操作。

2.1 验证

验证应该是前端程序员写正则表达式用的最多的方法吧,比如表单验证之类的。可以实现验证的方法有4种。

光说不练假把式,光练不说傻把式,又练又说才是真把式?。那么,请看下面的例子?:

2.1.1 使用test方法

如果正则表达式与指定的字符串匹配 ,返回true,否则false。得到的结果可以直接使用。

const str = "jing ke tong xue 666";

let reg1 = /d{4}/;
let res1 = reg1.test(str);
console.log(res1);
// => false

let reg2 = /ke/;
let res2 = reg2.test(str);
console.log(res2);
// => true

let reg3 = /d{3}/;
let res3 = reg3.test(str);
console.log(res3);
// => true

下面我们看一个匹配身份证的示例:

const str1 = "411199909096896";
const str2 = "411425199909096896";
const str3 = "41142519990909689x";
const str4 = "41142519990909689X";
const reg = /^(d{15}|d{17}[dxX])$/;
let res1 = reg.test(str1);
let res2 = reg.test(str2);
let res3 = reg.test(str3);
let res4 = reg.test(str4);
console.log(res1, res2, res3, res4);
// => true true true true

不过好在网上有很多可视化工具供我们使用,看一下下图可能就豁然开朗。

看图分析:这里竖杠|的优先级最低,所以正则分成了两部分d{15}d{17}[dxX]

d{15} 表示15位连续数字

d{17}[dxX] 表示17位连续数字,最后一位可以是数字,也可以大写或小写字母 "x"

下面我们再看一个稍微复杂一点的案例,匹配IPV4地址:

const str = "192.168.0.1";
let reg = /^((0{0,2}d|0?d{2}|1d{2}|2[0-4]d|25[0-5]).){3}(0{0,2}d|0?d{2}|1d{2}|2[0-4]d|25[0-5])$/;
let res = reg.test(str);
console.log(res);
// => true

上面那个正则表达式初看起来有点吓人哈?,这就是传说中的写正则不难,读正则难。先看下面可视化视图:

此正则表达式主体结构大致如下:

((…).){3}(…)

两个(...)是相同的内容,因此文字描述为3位数字.3数字.3位数字.3位数字

下面我们具体分析(...)也就是(0{0,2}d|0?d{2}|1d{2}|2[0-4]d|25[0-5]),多带带看一下这段代码的可视化视图:

看图分析,它是一个多选结构,分成5个部分:

0{0,2}d: 匹配一位数,包括 "0" 补齐的。比如,"9"、"09"、"009"

0?d{2}: 匹配两位数,包括 "0" 补齐的,也包括一位数

1d{2}: 匹配 "100" 到 "199"

2[0-4]d: 匹配 "200" 到 "249"

25[0-5]: 匹配 "250" 到 "255"

不要被看起来复杂的正则表达式吓到,乍看起来复杂的不要不要的,那我们就把它像洋葱一样,一层一层剥开它的心,拥抱它,然后扒光它。

2.1.2 使用exec方法

如果匹配成功,返回一个Array,否则返回null。得到的结果两次取反取得true或者false使用。

const str = "jing ke tong xue 666";

let reg1 = /d{4}/;
let res1 = reg1.exec(str);
console.log(res1);
console.log(!!res1);
// => null
// => false

let reg2 = /ke/;
let res2 = reg2.exec(str);
console.log(res2);
console.log(!!res2);
// => ["ke", index: 5, input: "jing ke tong xue 666", groups: undefined]
// => true

let reg3 = /d{3}/;
let res3 = reg3.exec(str);
console.log(res3);
console.log(!!res3);
// => ["666", index: 17, input: "jing ke tong xue 666", groups: undefined]
// => true
2.1.3 使用search方法

如果匹配成功,返回正则表达式在字符串中首次匹配项的索引(大于等于0),否则,返回 -1。如果为了和上面的方法保持一致返回true或false,这里需要借助一次按位非操作符(~)。

const str = "jing ke tong xue 666";

let reg1 = /d{4}/;
let res1 = str.search(reg1);
console.log(res1);
console.log(!!~res1);
// => -1
// => flase

let reg2 = /ke/;
let res2 = str.search(reg2);
console.log(res2);
console.log(!!~res2);
// => 5
// => true

const reg3 = /d{3}/;
let res3 = str.search(reg3);
console.log(res3);
console.log(!!~res3);
// => 17
// => true
2.1.4 使用match方法

如果匹配成功,返回一个Array,否则返回null。得到的结果两次取反取得true或者false使用。

const str = "jing ke tong xue 666";

let reg1 = /d{4}/;
let res1 = str.match(reg1);
console.log(res1);
console.log(!!res1);
// => null
// => flase

let reg2 = /ke/;
let res2 = str.match(reg2);
console.log(res2);
console.log(!!res2);
// => ["ke", index: 5, input: "jing ke tong xue 666", groups: undefined]
// => true

let reg3 = /d{3}/;
let res3 = str.match(reg3);
console.log(res3);
console.log(!!res3);
// => ["666", index: 17, input: "jing ke tong xue 666", groups: undefined]
// => true
2.2 替换

有时候找到了对象往往不是我们最终的目的,找到了对象做点什么才是我们想要的?。

这里我们看一看找到后的替换操作,我本人的常用场景就是格式化日期和删除空格。

// 把YYYY/MM/DD格式的日期替换成YYYY-MM-DD格式。
const str1 = "jing-ke-tong-xue";

let res1 = str1.replace(/-/g, " ");
console.log(res1);
// => jing ke tong xue

// 删除前后空格, 为了直观这里把前后空格替换成“删除了的空格”
const str2 = " jing ke tong xue ";
let res2 = str2.replace(/^s|s$/g, "删除了的空格");
console.log(res2);
// => 删除了的空格jing ke tong xue删除了的空格

// 据说下面这种方法速度比较快
const str3 = " jing ke tong xue ";
let res3 = str3.replace(/^s/, "删除了的空格").replace(/s$/, "删除了的空格");
console.log(res3);
// => 删除了的空格jing ke tong xue删除了的空格
2.3 切分

切萝卜切萝卜切切切,包饺子包饺子捏捏捏?。在这里,所谓切分就是把字符串切的一段一段的。

// 按空格切分
const str = "jing ke tong xue";

console.log(str.split(/s/));
// => ["jing", "ke", "tong", "xue"]

const str2 = "jing * ke ¥ tong ^xue";
console.log(str2.split(/s*s|s¥s|s^/))
// => ["jing", "ke", "tong", "xue"]
2.4 提取

有时候验证、替换、切分都不是目的,我们需要提取出来对我们有用的信息,那么就需要提取了。此时需要用到下篇会写到的“括号”(分组引用或者分组捕获)。

这里有一个正则将会贯穿本小节,我们先提前分析一下:

// 这里为了简单测试,只考虑了日期格式,没考虑日期的合理性
let reg = /(d{4})D(d{2})D(d{2})/g;

可视化视图:

看图说话:从上图可知这段正则的意思就是:4个数字后跟一个非数字再跟2个数字再跟一个非数字再跟2个数字。形如:4个数字-2个数字-2个数字格式。

2.4.1 使用exec方法

const str = "提取日期测试字符串,今天的日期是2018-04-04,今天的日期不是6666-66-66,提取日期测试字符串";
// 这里为了简单测试,只考虑了日期格式,没考虑日期的合理性
let reg = /(d{4})D(d{2})D(d{2})/g;
// 上篇文章有说:`exex`方法匹配到一次就会返回结果,想要下一个结果必须再次调用
console.log(reg.exec(str));
console.log(reg.exec(str));
// => ["2018-04-04", "2018", "04", "04", index: 16, input: "提取日期测试字符串,今天的日期是2018-04-04,今天的日期不是2018-40-40,提取日期测试字符串", groups: undefined]
// => ["6666-66-66", "6666", "66", "66", index: 34, input: "提取日期测试字符串,今天的日期是2018-04-04,今天的日期不是6666-66-66,提取日期测试字符串", groups: undefined]

2.4.2 使用match方法

const str = "提取日期测试字符串,今天的日期是2018-04-04,今天的日期不是6666-66-66,提取日期测试字符串";

// 这里为了简单测试,只考虑了日期格式,没考虑日期的合理性
let reg1 = /(d{4})D(d{2})D(d{2})/;
console.log(str.match(reg1));
// 没有g标识符返回结果与match无异
// => ["2018-04-04", "2018", "04", "04", index: 16, input: "提取日期测试字符串,今天的日期是2018-04-04,今天的日期不是6666-66-66,提取日期测试字符串", groups: undefined]

// 带有g标识符,结果数组只包含匹配结果
// 这里为了简单测试,只考虑了日期格式,没考虑日期的合理性
let reg2 = /(d{4})D(d{2})D(d{2})/g;
console.log(str.match(reg2));
// => ["2018-04-04", "6666-66-66"]

2.4.3 使用replace方法

在上篇文章步《JavaScript正则表达式学习笔记(一) - 理论基础》的理论基础中提到replace方法的第二个参数可以是一个函数,函数接收的参数包含我们需要的信息。

const str = "提取日期测试字符串,今天的日期是2018-04-04,今天的日期不是6666-66-66,提取日期测试字符串";

// 这里为了简单测试,只考虑了日期格式,没考虑日期的合理性
let reg1 = /(d{4})D(d{2})D(d{2})/;

let res1 = [];
str.replace(reg1, function(match, year, month, day, offset, string) {
    res1.push(match, year, month, day, offset, string);
});
console.log(res1);
// => ["2018-04-04", "2018", "04", "04", 16, "提取日期测试字符串,今天的日期是2018-04-04,今天的日期不是6666-66-66,提取日期测试字符串"]

// 这里为了简单测试,只考虑了日期格式,没考虑日期的合理性
let reg2 = /(d{4})D(d{2})D(d{2})/g;

let res2 = [];
str.replace(reg2, function(match, year, month, day, offset, string) {
    res2.push(match);
});
console.log(res2);
// => ["2018-04-04", "6666-66-66"]

从输出效果来看,replace方法可以达到模拟match方法的效果。这就是传说中的条条大路通罗马No roads can"t lead to Rome(哼...哼哼...翻译是我故意的嗷?)

2.4.4 使用test方法

此方法会用到RegExp.$1-$9这一非标属性,这里只是使用,下文会做介绍。
const str = "提取日期测试字符串,今天的日期是2018-04-04,今天的日期不是6666-66-66,提取日期测试字符串";

// 这里为了简单测试,只考虑了日期格式,没考虑日期的合理性
let reg = /(d{4})D(d{2})D(d{2})/;

reg.test(str);

// RegExp.$1-$9非标属性,但是目的达到了,请勿用于生产环境
let res = [RegExp.$1, RegExp.$2, RegExp.$3];

console.log(res);
// => ["2018", "04", "04"]

2.4.4 使用search方法

此方法会用到RegExp.$1-$9这一非标属性,这里只是使用,同样留到下文介绍。
const str = "提取日期测试字符串,今天的日期是2018-04-04,今天的日期不是6666-66-66,提取日期测试字符串";

// 这里为了简单测试,只考虑了日期格式,没考虑日期的合理性
let reg = /(d{4})D(d{2})D(d{2})/;

str.search(reg);

// RegExp.$1-$9非标属性,但是目的达到了,请勿用于生产环境
let res = [RegExp.$1, RegExp.$2, RegExp.$3];

console.log(res);
// => ["2018", "04", "04"]

到这里正则表达式的验证、替换、切分、提取已经介绍完了,有些操作是取巧的做法,也不建议在生产环境使用,不过某些特殊情况除外。至于哪些情况是特殊情况,具体问题具体分析吧?。

三. 注意要点

在本文 1.2 小节提到了6种可以用于正则操作的方法,RegExp提供2种,String提供4种。本章节就围绕这几种方法展开。

3.1 match方法返回结果的格式不一致问题
这个问题上文《JavaScript正则表达式学习笔记(一) - 理论基础》也有体现,这里再多带带拿来说一说,以加深记忆。
const str = "提取日期测试字符串,今天的日期是2018-04-04,今天的日期不是6666-66-66,提取日期测试字符串";

// 这里为了简单测试,只考虑了日期格式,没考虑日期的合理性
let reg1 = /(d{4})D(d{2})D(d{2})/;
console.log(str.match(reg1));
// => ["2018-04-04", "2018", "04", "04", index: 16, input: "提取日期测试字符串,今天的日期是2018-04-04,今天的日期不是6666-66-66,提取日期测试字符串", groups: undefined]

// 这里为了简单测试,只考虑了日期格式,没考虑日期的合理性
let reg2 = /(d{4})D(d{2})D(d{2})/g;
console.log(str.match(reg2));
// => ["2018-04-04", "6666-66-66"]

// 这个正则只是来客串说明问题,没有其他意义
let reg3 = /.^/;
console.log(str.match(reg3));
// => null

// 这个正则只是来客串说明问题,没有其他意义
let reg4 = /.^/g;
console.log(str.match(reg4));
// => null

当没有g标识符时,返回的结果为标准匹配格式,包含完整的匹配信息。

当有g标识符时,返回的结果为所有匹配结果组成的数组。

当匹配不成功时,无论有没有标识符(包括igmyu的任意组合),都返回null

3.2 searchmatch的参数问题

话不多说,先看代码:

// 为了说明问题,日期格式选择了用“.”连接,因为“.”在正则中属于元字符
const str = "2018.04.04";

console.log(str.search("."));
console.log(str.search(/./));
console.log(str.search("."));
console.log(str.search(/./));
// => 0
// => 0
// => 4
// => 4

console.log(str.match("."));
console.log(str.match(/./));
console.log(str.match("."));
console.log(str.match(/./));
// => ["2", index: 0, input: "2018.04.04", groups: undefined]
// => ["2", index: 0, input: "2018.04.04", groups: undefined]
// => [".", index: 4, input: "2018.04.04", groups: undefined]
// => [".", index: 4, input: "2018.04.04", groups: undefined]

console.log(str.replace(".", "-"));
console.log(str.replace( /./, "-"));
console.log(str.replace(".", "-"));
console.log(str.replace(/./, "-"));
// => 2018-04.04
// => -018.04.04
// => 2018-04.04
// => 2018-04.04

console.log(str.split("."));
console.log(str.split(/./));
console.log(str.split("."));
console.log(str.split(/./));
// => ["2018", "04", "04"]
// => ["", "", "", "", "", "", "", "", "", "", ""]
// => ["2018.04.04"]
// => ["2018", "04", "04"]

从上述代码可以看出search方法和match方法会将接收到的字符串参数转为正则,而replace方法和split方法不会转换,所以使用时请注意。

3.3 execmatch的王者之争

王者之争第一回合:

const str = "2018-04-04";

let reg1 = /(d+)/;
let reg2 = /(d+)/g;

console.log(reg1.exec(str));
console.log(reg2.exec(str));
// => ["2018", "2018", index: 0, input: "2018-04-04", groups: undefined]
// => ["2018", "2018", index: 0, input: "2018-04-04", groups: undefined]

console.log(str.match(reg1));
console.log(str.match(reg2));
// => ["2018", "2018", index: 0, input: "2018-04-04", groups: undefined]
// ["2018", "04", "04"]

g标识符对exec方法没有产生影响,但是改变了match方法的行为。没主见啊,没主见。

没有g标识符时,match返回标准匹配参数,有g标识符时返回了匹配结果的集合。但是对于exec方法无论有没有g标识符都返回了同样的结果。不智能啊,不智能。

这一回合实力相差不大,看不出孰优孰劣。

王者之争第二回合:

const str = "2018-04-04";

let reg1 = /(d+)/;
let reg2 = /(d+)/g;

console.log(reg2.lastIndex);
console.log(reg2.exec(str));
// => 0
// => ["2018", "2018", index: 0, input: "2018-04-04", groups: undefined]

console.log(reg2.lastIndex);
console.log(reg2.exec(str));
// => 4
// => ["04", "04", index: 5, input: "2018-04-04", groups: undefined]

console.log(reg2.lastIndex);
console.log(reg2.exec(str));
// => 7
// => ["04", "04", index: 8, input: "2018-04-04", groups: undefined]

console.log(reg2.lastIndex);
console.log(reg2.exec(str));
// => 10
// => null

// 上一次匹配失败,这一次从头开始
console.log(reg2.lastIndex);
console.log(reg2.exec(str));
// => 0
// => ["2018", "2018", index: 0, input: "2018-04-04", groups: undefined]

// 没有g标识符时match方法每次都从第一位开始匹配
console.log(str.match(reg1));
console.log(str.match(reg1));
// => ["2018", "2018", index: 0, input: "2018-04-04", groups: undefined]
// => ["2018", "2018", index: 0, input: "2018-04-04", groups: undefined]

这一回合没有悬念,exec方法胜出。不过上面的写法未免太过繁琐。请看下面当exec遇上while

const str = "2018-04-04";

let reg = /(d+)/g;

// 结合while流程控制语句
let res;
while (res = reg.exec(str)) {
    console.log(reg.lastIndex, res);
}
// => 4 ["2018", "2018", index: 0, input: "2018-04-04", groups: undefined]
// => 7 ["04", "04", index: 5, input: "2018-04-04", groups: undefined]
// => 10 ["04", "04", index: 8, input: "2018-04-04", groups: undefined]

王者之争至此结束。exec要比match强大那么一点哈。

3.4 除了exec方法,g标识符对test方法也有影响

上篇文章曾经提到 lastIndex 是正则表达式的一个可读可写的整型属性,用来指定下一次匹配的起始索引。 也就是说,只要正则还是那个正则,当存在g标识符的时候lastIndex都会做出相应的改变,要匹配的字符串可以不是同一个。

const str1 = "2018-04-04";
const str2 = "2018-04-05";
const str3 = "2018-04-06";
const str4 = "2018-04-07";
const str5 = "2018-04-08";

let reg = /(d+)/g;

console.log(reg.lastIndex);
console.log(reg.test(str1));
// => 0
// => true

console.log(reg.lastIndex);
console.log(reg.test(str2));
// => 4
// => true

console.log(reg.lastIndex);
console.log(reg.test(str3));
// => 7
// => true

console.log(reg.lastIndex);
console.log(reg.test(str4));
// => 10
// => false

console.log(reg.lastIndex);
console.log(reg.test(str5));
// => 0
// => true

当没有g操作符时,始终从0开始匹配,这里就不做演示了。

3.5 split方法注意事项
const str = "2018-04-04";

console.log(str.split("-"));
console.log(str.split("-", 2));
console.log(str.split("-", 10));
// => ["2018", "04", "04"]
// => ["2018", "04"]
// => ["2018", "04", "04"]

let reg1 = /-/;
let reg2 = /(-)/;

console.log(str.split(reg1));
console.log(str.split(reg2));
// => ["2018", "04", "04"]
// => ["2018", "-", "04", "-", "04"]

split方法可以接收第二个参数指定返回数组长度,第二个参数只有小于实际返回数组长度时才生效。

split接收的正则表达式种包含分组模式时,返回的结果数组包含分组匹配项。

3.6 强大的replace方法

replace方法不但可以接收一个函数作为第二个参数(前面已经体现,这儿不重复示例),也可以接收一个字符串作为第二个参数。此处的字符串除了是一个普通的替换字符串之外,也可以是一个特殊变量。本小节以实际示例介绍一下这几个特殊变量(第一篇理论基础有提及)。

变量名 代表的值
$$ 插入一个 "$"。
$& 插入匹配的子串。
$` 插入当前匹配的子串左边的内容。
$" 插入当前匹配的子串右边的内容。
$n 匹配第n个分组里捕获的文本,n是不大于100的正整数。

哇哈,是时候表演真正的技术了,下面我们就来看看replace的能力。

const str1 = "3 6 9";
        
let reg1 = /d/g;
// 被$包围
console.log(str1.replace(reg1, "$$$&$$"));
// => $3$ $6$ $9$

// 分身
console.log(str1.replace(reg1, "$&$&$&"));
// => 333 666 999

// 分身相加
let reg2 = /(d)s(d)s(d)/;
console.log(str1.replace(reg2, "$1$1$1+$2$2$2$2=$3$3$3"));
// => 333+6666=999

// 你爱我我爱你
console.log(str1.replace(reg2, "$1$1$1+$2$2$2=$2$2$2+$1$1$1=$3$3$3=>?"));
// => 333+666=666+333=999=>?

const str2 = "3?6?9";
let reg3 = /?|?/g;
console.log(str2.replace(reg3, "($&的左边是: $`, 右边是: $")"));
// => 3(?的左边是: 3, 右边是: 6?9)6(?的左边是: 3?6, 右边是: 9)9

突然间感觉王者之争不应该只让execmatch参加,replace这么强大,也应该参与其中的嘛?。

3.7 构造函数和字面量问题

这里没有什么悬念,一般建议优先使用字面量的方式创建正则表达式,因为构造函数中需要对元字符转义,会多写很多的反斜杠。当然特殊情况还是要用构造函数。

const str = "2018-04-04";

let reg1 = /(d{4})D(d{2})D(d{2})/;
console.log(reg1);
console.log(reg1.test(str));
// => /(d{4})D(d{2})D(d{2})/
// => true

let reg2 = new RegExp("(d{4})D(d{2})D(d{2})");
console.log(reg2);
console.log(reg2.test(str));
// => /(d{4})D(d{2})D(d{2})/
// => false

let reg3 = new RegExp("(d{4})D(d{2})D(d{2})");
console.log(reg3);
console.log(reg3.test(str));
// => /(d{4})D(d{2})D(d{2})/
// => true

下面是特殊情况:

let name = "user name";

// user name是一个变量
const str = "2018-04-04 user name";

// 在字面量中,无法实现动态拼接
let reg1 = /(d{4})D(d{2})D(d{2})D + name/;
console.log(reg1);
console.log(reg1.test(str));
// => /(d{4})D(d{2})D(d{2})D + name/
// => flase

let reg2 = new RegExp("(d{4})D(d{2})D(d{2})D" + "(" + name + ")");
console.log(reg2);
console.log(reg2.test(str));
// => /(d{4})D(d{2})D(d{2})D(user name)/
// => true
3.8 几个非标属性

上面用到了RegExp.$1这一非标属性,所谓非标属性,就是此属性不符合当前任何标准规范。所以,请尽量不要在生产环境中使用,除非特殊情况并且你能保证以后也不会出错。

属性 别名 说明
RegExp.$1-$9 静态、只读属性。包含括号子串匹配的正则表达式的静态和只读属性。只有在正确匹配的情况下才会改变。虽然括号可以无限,但是此属性最多只能匹配9个。
RegExp.input RegExp.$_ 静态属性,含有正则表达式最近一次所匹配的字符串。当正则表达式上搜索的字符串发生该变,并且字符串匹配时,input 属性的值会修改。
RegExp.lastMatch RegExp["$&"] 静态、只读属性。含有最近一次匹配到的字符串。属性的值是只读的,会在匹配成功时修改。
RegExp.lastParen RegExp["$+"] 静态、只读属性,包含匹配到的最后一个子串。会在匹配成功时修改。
RegExp.leftContext RegExp["$`"] 静态、只读属性。含有最新匹配的左侧子串。 会在匹配成功时修改。
RegExp.rightContext RegExp["$""] 静态、只读属性。含有最新匹配的右侧子串。 会在匹配成功时修改。

这几个属性平时也基本用不到,了解了解总是好的,请看下面示例:

const str = "a1b2c3d4e5f6";
let reg = /([a-f])([1-6])/g;

// 为了倒数第二个有输出,这里执行两次exec方法
console.log(reg.exec(str));
console.log(reg.exec(str));
// ["a1", "a", "1", index: 0, input: "a1b2c3d4e5f6", groups: undefined]
// ["b2", "b", "2", index: 2, input: "a1b2c3d4e5f6", groups: undefined]

console.log(RegExp.$1);
console.log(RegExp.$2);
// => b
// => 2

console.log(RegExp.input);
console.log(RegExp.$_);
// => a1b2c3d4e5f6
// => a1b2c3d4e5f6

console.log(RegExp.lastMatch);
console.log(RegExp["$&"]);
// => b2
// => b2

console.log(RegExp.lastParen);
console.log(RegExp["$+"]);
// => 2
// => 2

console.log(RegExp.leftContext);
console.log(RegExp["$`"]);
// => a1
// => a1

console.log(RegExp.rightContext);
console.log(RegExp["$""]);
// => c3d4e5f6
// => c3d4e5f6
四. 奇技淫巧

本文写的也挺长的,剩下的准备再写一篇终结JavaScript正则表达式部分的内容。那么本文就先用一个真是案例来做结尾吧。

JavaScript常用的类型判断实现

下面这段代码是在某个框架源码中见到的,初见之时倍感惊艳(原谅我入行不久见识短?),其中也用到上文提到的split方法,特拿出来分享一下。

let utils = Object.create(null);
const types = "Boolean|Number|String|Function|Array|Date|RegExp|Object|Error";
types.split("|").forEach(type => {
    utils["is" + type] = obj => {
        return Object.prototype.toString.call(obj) == "[object " + type + "]";
    };
});
console.log(utils.isBoolean("true"));
console.log(utils.isBoolean(true));

虽然可以将Boolean|Number|String|Function|Array|Date|RegExp|Object|Error存储为数组减少一次split切分操作,但是这样似乎多了点黑科技的感觉。

由于本同学能力有限,不足之处还望各位大佬同学指正。

至此,本文完。

第一篇完结: JavaScript正则表达式学习笔记(一) - 理论基础

第二篇完结(就是本文):JavaScript正则表达式学习笔记(二) - 打怪升级

第三篇待更:JavaScript正则表达式学习笔记(三) - 终章

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/94080.html

相关文章

  • [打怪升级]小程序评论回复和发贴功能实战(一)

    摘要:往期回顾打怪升级小程序评论回复和发贴功能实战二填坑手册小程序生成海报一拆弹时刻小程序生成海报二填坑手册小程序目录结构和组件使用心得 showImg(https://segmentfault.com/img/remote/1460000019733090?w=818&h=516); 在学习成长的过程中,常常会遇到一些自己从未接触的事物,这就好比是打怪升级,每次打倒一只怪,都会获得经验,让...

    YJNldm 评论0 收藏0
  • 前端计划——Codewars的一些JavaScript题集

    摘要:解题思路使用正则表达式地址转问题描述把输入的地址转换为格式比如解题思路使用位移运算符方法方法找零钱问题问题描述电影院票价为,而观众手中的美元面值有,。解题思路为每个面值的美元设定初始数量,然后开始对每个数组值遍历,根据不同面值设定不同规则。 关于Codewars:这是一个用于编程练习的网站,支持多种编程语言。网站首次注册需要完成两道题目,之后进入系统,类似于打怪升级。通过完成题目来实现...

    jayce 评论0 收藏0
  • js正则达式学习笔记

    摘要:分组小括号功能较多有三种功能,作用一,把单独的项组合成子表达式。作用三,允许后面引用前面的表达式。如果这时的正则表达式是一个非全局的,那么和正则表达式方法拿到的结果相同。,也可以接受一个正则表达式作为参数。 正则表达式创建字面量创建,a = /abc/gim;构造函数创建,a = new RegExp(abc,gim)正则表达式中的特殊字符,[...] 方括号中的任意字符[^...] ...

    superPershing 评论0 收藏0
  • 郑方方打怪升级日记 — 初识HTML5与CSS3

    摘要:任务名称响应式砸蛋页面任务背景前辈方方啊最近项目也没什么事情你看这个砸蛋页面不是很好看要不你做一个响应式砸蛋页面吧系统郑方方接下前辈的任务郑方方自动解析任务步骤任务响应式砸蛋页面与入门阅读秘籍响应式布局制作层搭配搭配控制器完成任务人物背 任务名称:响应式砸蛋页面 任务背景 前辈:方方啊,最近项目也没什么事情,你看这个砸蛋页面不是很好看,要不你做一个响应式砸蛋页面吧? 系统:郑方方接下前...

    spademan 评论0 收藏0
  • 郑方方打怪升级日记 — 初识HTML5与CSS3

    摘要:任务名称响应式砸蛋页面任务背景前辈方方啊最近项目也没什么事情你看这个砸蛋页面不是很好看要不你做一个响应式砸蛋页面吧系统郑方方接下前辈的任务郑方方自动解析任务步骤任务响应式砸蛋页面与入门阅读秘籍响应式布局制作层搭配搭配控制器完成任务人物背 任务名称:响应式砸蛋页面 任务背景 前辈:方方啊,最近项目也没什么事情,你看这个砸蛋页面不是很好看,要不你做一个响应式砸蛋页面吧? 系统:郑方方接下前...

    justCoding 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<