Skip to content
Go back

强大的正则表达式

Published:  at  05:25 PM

文章目录

正则表达式

正则表达式最常用于处理和校验字符串,是非常好用的简便工具。如果用的熟练,能够极大的提高工作效率。并且在阅读前端框架源码的时候经常也会遇到正则,记录在此以备忘。

基础的正则表达式示例

let reg = /^\d{3}$/
console.log(reg.test("123")) // true
console.log(reg.test("a12")) // false
console.log(reg.test("1234")) // false

/^\d{3}$/表示匹配字符串是以三个数字开头和结尾的的字符串。
^表示开头的位置。
$表示结束的位置。
\d表示[0-9]的数字。
{3}表示前面的符号出现的3次。

惰性匹配和贪婪匹配

所谓惰性或者贪婪都是在全局搜索符g的修饰下才会有的。 在量词后面追加一个?,就可使贪婪变为懒惰

var greedyReg = /\d{2,5}/g; 
var lazyReg = /\d{2,5}?/g;
var str = "12 123 123456789 "
console.log(str.match(greedyReg))
// ["12", "123", "12345", "6789"]
console.log(str.match(lazyReg))
// ["12", "12", "12", "34", "56", "78"]

// 管道符默认是懒惰的,前面的被匹配之后,就不再继续了
var pipChar = /good|goodbye/;
var str = 'goodbye my love';
console.log(str.match(pipChar));
// [good]

正则实用实例

// 匹配16进制颜色
var colorReg = /[0-9a-fA-F]{6}|[0-9a-fA-F]{3}/

// 匹配时间
var timeReg = /([01][0-9]|2[0-3])-[0-5][0-9]$/

一、字符组和量词

1. 两种模糊匹配

// 横向模糊

var regex = /ab{2,5}c/g;
var string = "abc abbc abbbc abbbbc abbbbbc abbbbbbc";
console.log( string.match(regex) );
// => ["abbc", "abbbc", "abbbbc", "abbbbbc"]


// 纵向模糊

var regex = /a[123]b/g;
var string = "a0b a1b a2b a3b a4b";
console.log( string.match(regex) );
// => ["a1b", "a2b", "a3b"]

2. 字符组

// [123] 表示1、2、3中的一个
// - 在[]中表示范围
// ^ 在[]中表示不匹配
字符组具体含义
\d[0-9]
\D[^0-9]
\w[0-9a-zA-Z_]
\W[^0-9a-zA-Z]
\s空白符 space缩写
\S非空白符
.表示几乎所有字符

3. 量词

惰性量词贪婪量词
{m, n}?{m, n}
{m, }?{m, } 等价于 {m,m}
??? 等价于 {0,1}
+?+ 等价于 {1,}
*?* 等价于 {0,}

惰性匹配,就是尽可能少的匹配

4. 多选分支

分支结构是惰性的,即当前面的匹配上了,后面的就不再尝试了。

例子汇总一:

// 匹配 16 进制颜色值 #fff #fa541d

var reg = /#([a-zA-Z0-9]{6}|[a-zA-Z0-9]{3})/


// 匹配时间  23:59  7:9

var reg = /^([01][0-9]|2[0-3]):[0-5][0-9]$/
var reg = /^(0?[0-9]|1[0-9]|2[0-3]):(0?[0-9]|[1-5][0-9])$/ // 可以省略第一位0

// 匹配日期 yyyy-mm-dd

var reg = /^[0-9]{4}-(0[1-9]|1[0-2])-(0[0-9]|[12][0-9]|3[01])$/

// windows操作系统路径  可能是文件 也可能是文件夹

// var reg = ^[a-zA-Z]:\\([^\\:*<>|"?\r\n/]+\\)*([^\\:*<>|"?\r\n/]+)?;

// 匹配id <div id="container" class="main"></div>

var reg = /.*id="[^"]*"/

// 密码长度 6-12 位,由数字、小写字符和大写字母组成,但必须至少包括 2 种字符。

var reg = /(?=.*[0-9])(?=.*[a-z])|(?=.*[0-9])(?=.*[A-Z])|(?=.*[a-z])(?=.*[A-Z])^[0-9a-zA-Z]{6,12}$/

var reg = /(?![0-9]{6,12})|(?![a-z]{6,12})|(?![A-Z]{6,12})^[0-9a-z-A-Z]{6,12}$/

二、位置匹配

在 ES5 中,共有 6 个锚:^、$、\b、\B、(?=p)、(?!p)

^ 匹配开头,在多行匹配中匹配行开头

$(美元符号)匹配结尾,在多行匹配中匹配行结尾。

\b 是单词边界,具体就是 \w 与 \W 之间的位置,也包括 \w 与 ^ 之间的位置,和 \w 与 $ 之间的位置。

(?=p) p前面的位置

(?!p) 不是p前面的位置

(?<=p) p后面的位置

(?<!p) 不是p后面的位置

var result = "hello".replace(/(?=l)/g, '#');
console.log(result);
// => "he#l#lo"

例子汇总二:

// 不匹配任何东西
var reg = /.^/  //要求有个字符,但是字符的后面是开头,这样的字符是不存在的

// 数字的千分位

// var reg = /(?!^)(?=\d{3}+$)/
var reg1 = /\B(?=(\d{3})+\b)/

// 验证密码问题
var reg = /(?=.*[0-9])^[0-9a-zA-Z]{6,12}$/
var reg1 = /(?!^[0-9]{6,12}$)|(?!^[a-z]{6,12}$)|(?!^[A-Z]{6,12}$)^[0-9a-zA-Z]{6,12}$/

三、圆括号的作用

// 分组
var reg = /(ab)+/g
var string = "ababa abbb ababab";
console.log( string.match(regex) );
// => ["abab", "ab", "ababab"]

// 分支结构
var regex = /^I love (JavaScript|Regular Expression)$/;
console.log( regex.test("I love JavaScript") );
console.log( regex.test("I love Regular Expression") );

// 提取数据
var regex = /(\d{4})-(\d{2})-(\d{2})/;
var string = "2017-06-12";
console.log( string.match(regex) );
// => ["2017-06-12", "2017", "06", "12", index: 0, input: "2017-06-12"]
var string = "2017-06-12";
regex.test(string); // 正则操作即可,例如
//regex.exec(string);
//string.match(regex);
console.log(RegExp.$1); // "2017"
console.log(RegExp.$2); // "06"
console.log(RegExp.$3); // "12"

// 替换
var regex = /(\d{4})-(\d{2})-(\d{2})/;
var string = "2017-06-12";
var result = string.replace(regex, "$2/$3/$1"); //$1、$2、$3 指代相应的分组
// 等价于
var result = string.replace(regex, function () {
 return RegExp.$2 + "/" + RegExp.$3 + "/" + RegExp.$1;
});
// 等价于
var result = string.replace(regex, function (match, year, month, day) {
 return month + "/" + day + "/" + year;
});
console.log(result);
// => "06/12/2017"

反向引用

//反向引用 \1应该代表的第一个分组
var regex = /\d{4}(-|\/|\.)\d{2}\1\d{2}/;
var string1 = "2017-06-12";
var string2 = "2017/06/12";
var string3 = "2017.06.12";
var string4 = "2016-06/12";
console.log( regex.test(string1) ); // true
console.log( regex.test(string2) ); // true
console.log( regex.test(string3) ); // true
console.log( regex.test(string4) ); // false

//括号嵌套
var regex = /^((\d)(\d(\d)))\1\2\3\4$/;
var string = "1231231233";
console.log( regex.test(string) ); // true
console.log( RegExp.$1 ); // 123
console.log( RegExp.$2 ); // 1
console.log( RegExp.$3 ); // 23
console.log( RegExp.$4 ); // 3
// \1,是第一个分组内容,那么看第一个开括号对应的分组是什么,是 "123",
// \2,找到第2个开括号,对应的分组,匹配的内容是 "1",
// \3,找到第3个开括号,对应的分组,匹配的内容是 "23",
// \4,找到第3个开括号,对应的分组,匹配的内容是 "3"。

// \10 表示什么呢
var regex = /(1)(2)(3)(4)(5)(6)(7)(8)(9)(#) \10+/;
var string = "123456789# ########"
console.log( regex.test(string) );
// => true
// 如果真要匹配 \1 和 0 的话,请使用 (?:\1)0 或者 \1(?:0)。

// 引用不存在的分组会怎样?
var regex = /\1\2\3\4\5\6\7\8\9/;
console.log( regex.test("\1\2\3\4\5\6\7\8\9") );
// => true

// 分组后面有量词会怎样?
var regex = /(\d)+/;
var string = "12345";
console.log( string.match(regex) );
// => ["12345", "5", index: 0, input: "12345"]
var regex = /(\d)+ \1/;
console.log( regex.test("12345 1") );
// => false
console.log( regex.test("12345 5") );
// => true

非捕获括号

之前出现的括号,都会捕获它们匹配到的数据,以便后续引用,因此也称它们是捕获型分组和捕获型分支。

var regex = /(?:ab)+/g;
var string = "ababa abbb ababab";
console.log( string.match(regex) );
// => ["abab", "ab", "ababab"]

var pattern=/(ab)\w+(ba)/;
console.log("abcba_".replace(pattern,"$1"));
/*
*结果"ab_";匹配到的字符被第一个分组(ab)
*替换
*/
var pattern2=/(?:ab)\w+(ba)/;
console.log("abcba_".replace(pattern2,"$1"));
// 在分组内使用?:可以产生没有编号的分组。
/*
*结果"ba_";第一次分组内加入了?:,产生的是一个
*没有编号的分组,所以$1匹配的字符是第二个分组,
*也就是第一个编号分组(ba)相匹配的文本内容
*/

例子汇总三:

// 字符串trim
function trim(str){
    return str.replace(/^\s*(.*?)\s*$/g, '$1')
}

// 将每个单词的首字母转换为大写
function titleize(str){
    return str.match(/(?:^|\s)\w/g, function(c){
        return c.toUpperCase()
    })
}
console.log( titleize('my name is epeli') );
// => "My Name Is Epeli"

// 驼峰化

function titleize(str){
    return str.match(/[-_\s]+(.)*\w/g, function (c) {
        return c.toUpperCase()
    })
}

四、回溯

本质上就是深度优先搜索算法。其中退到之前的某一步这一过程,我们称为“回溯”。从上面的描述过程中,可以看出,路走不通时,就会发生“回溯”。即,尝试匹配失败时,接下来的一步通常就是回溯。

个人理解,回溯,就是因为量词有时候匹配了太多内容,导致后面的字符匹配不上,然后匹配过的结果又要一个一个的回退,看是否能够与后面的正则内容匹配上。

var reg = /".*"/
// 为了减少一些不必要的回溯,可以把正则修改为 /"[^"]*"/。

五、正则表达式的拆分

1. 结构和操作符

操作符描述操作符优先级
转义符\1
括号和方括号(…)、(?:…)、(?=…)、(?!…)、[…]2
量词限定符{m}、{m,n}、{m,}、?、*、+3
位置和序列^、$、\元字符、一般字符4
管道符(竖杠)|5

2. 匹配字符串整体问题

因为是要匹配整个字符串,我们经常会在正则前后中加上锚 ^ 和 $。

var reg = /^abc|bcd$/

3. 量词连缀问题

// var reg = /^[abc]{3}+$/   会报错
var reg = /^([abc]{3})+$/

4. 元字符转义问题

// 所有结构里,用到的元字符总结如下:
// ^ $ . * + ? | \ / ( ) [ ] { } = ! : - 
var string = "^$.*+?|\\/[]{}=!:-,";
var regex = /\^\$\.\*\+\?\|\\\/\[\]\{\}\=\!\:\-\,/;
console.log( regex.test(string) );
// => true


// ^、$、.、*、+、?、|、\、/ 等字符,只要不在字符组内,都需要转义的。

// 匹配 "[abc]" 和 "{3,5}"
var string = "[abc]";
var regex = /\[abc]/g;
console.log( string.match(regex)[0] );
// => "[abc]"
var string = "{,3}";
var regex = /{,3}/g;
console.log( string.match(regex)[0] );
// => "{,3}"

不要无所不用正则,正则也有它的局限。

比如一个大正则,使用多个小正则。

注意带有g修饰符的正则在使用match和test时,会有lastIndex这个数值,并不会每次都从同一个位置进行测试

使用具体型字符组来代替通配符,来消除回溯

5. 使用具体型字符组来代替通配符,来消除回溯

匹配字符串 123”abc”456 中的 “abc”。 /”.”/,会在第 3 阶段产生 4 次回溯 /”.?”/,会产生 2 次回溯 要使用具体化的字符组,来代替通配符.,以便消除不必要的字符,此时使用正则 /”[^”]*”/,即可

6. 使用非捕获型分组

捕获分组需要内存来保存它们。不需要使用分组引用和反向引用时,此时可以使用非捕获分组。

例如,/^[-]?(\d.\d+|\d+|.\d+)$/ 可以修改成:/^[-]?(?:\d.\d+|\d+|.\d+)$/。

7. 独立出确定字符

例如,/a+/ 可以修改成 /aa*/。 因为后者能比前者多确定了字符 “a”。这样会在第四步中,加快判断是否匹配失败,进而加快移位的速度。

8. 提取分支公共部分

比如,/^abc|^def/ 修改成 /^(?:abc|def)/。 又比如, /this|that/修改成 /th(?:is|at)/。 这样做,可以减少匹配过程中可消除的重复。

9. 减少分支的数量,缩小它们的范围

/red|read/ 可以修改成 /rea?d/。 此时分支和量词产生的回溯的成本是不一样的。但这样优化后,可读性会降低的。

六、正则表达式编程

1. 正则的四种操作

验证

var regex = /\d/;
var string = "abc123";
console.log( !!~string.search(regex) );
console.log( regex.test(string) );
console.log( !!string.match(regex) );
console.log( !!regex.exec(string) );
// => true

切分

var regex = /,/;
var string = "html,css,javascript";
console.log( string.split(regex) );
var regex = /\D/;
console.log( "2017/06/26".split(regex) );
console.log( "2017.06.26".split(regex) );
console.log( "2017-06-26".split(regex) );
// => ["2017", "06", "26"]
// => ["2017", "06", "26"]
// => ["2017", "06", "26"]

提取

// match
var regex = /^(\d{4})\D(\d{2})\D(\d{2})$/;
var string = "2017-06-26";
console.log( string.match(regex) );
// =>["2017-06-26", "2017", "06", "26", index: 0, input: "2017-06-26"]
console.log( regex.exec(string) );
// =>["2017-06-26", "2017", "06", "26", index: 0, input: "2017-06-26"]

var regex = /^(\d{4})\D(\d{2})\D(\d{2})$/;
var string = "2017-06-26";
var date = [];
string.replace(regex, function (match, year, month, day) {
 date.push(year, month, day);
});
console.log(date);
// => ["2017", "06", "26"]

替换

var string = "2017-06-26";
var today = new Date( string.replace(/-/g, "/") );
console.log( today );
// => Mon Jun 26 2017 00:00:00 GMT+0800 (中国标准时间)

2. 相关API

String#search String#split String#match String#replace RegExp#test RegExp#exec

search 和 match,会把字符串转换为正则的。

var string = "2017.06.27";
console.log( string.search(".") );
// => 0
//需要修改成下列形式之一
console.log( string.search("\\.") );
console.log( string.search(/\./) );
// => 4
// => 4

3. match 返回结果的格式

与正则对象是否有修饰符 g 有关。

var string = "2017.06.27";
var regex1 = /\b(\d+)\b/;
var regex2 = /\b(\d+)\b/g;
console.log( string.match(regex1) );
console.log( string.match(regex2) );
// => ["2017", "2017", index: 0, input: "2017.06.27"]
// => ["2017", "06", "27"]

4. exec 比 match 更强大

正则实例的两个方法 exec、test,当正则是全局匹配时,每一次匹配完成后,都会修改 lastIndex,如果没有 g,自然都是从字符串第 0 个字符处开始尝试匹配。

var string = "2017.06.27";
var regex2 = /\b(\d+)\b/g;
var result;
while ( result = regex2.exec(string) ) {
 console.log( result, regex2.lastIndex );
}
// => ["2017", "2017", index: 0, input: "2017.06.27"] 4
// => ["06", "06", index: 5, input: "2017.06.27"] 7
// => ["27", "27", index: 8, input: "2017.06.27"] 10

5. test 整体匹配时需要使用 ^ 和 $

console.log( /123/.test("a123b") );
// => true
console.log( /^123$/.test("a123b") );
// => false
console.log( /^123$/.test("123") );
// => true

6. split 相关注意事项

var string = "html,css,javascript";
console.log( string.split(/,/, 2) );
// =>["html", "css"]
console.log( string.split(/(,)/) );
// =>["html", ",", "css", ",", "javascript"]

7. replace 是很强大的

replace 有两种使用形式,这是因为它的第二个参数,可以是字符串,也可以是函数。

当第二个参数是字符串时,如下的字符有特殊的含义:

属性 描述 $1,$2,…,$99 匹配第 1-99 个 分组里捕获的文本 $& 匹配到的子串文本 $` 匹配到的子串的左边文本 $’ 匹配到的子串的右边文本 $$ 美元符号$$

var result = "2,3,5".replace(/(\d+),(\d+),(\d+)/, "$3=$1+$2");
console.log(result);
// => "5=2+3"

8. 修饰符

修饰符 描述 g 全局匹配,即找到所有匹配的,单词是 global。 i 忽略字母大小写,单词是 ingoreCase。 m 多行匹配,只影响 ^ 和 $,二者变成行的概念,即行开头和行结尾。单词是 multiline。

var regex = /\w/img;
console.log( regex.global );
console.log( regex.ignoreCase );
console.log( regex.multiline );
// => true
// => true
// => true

正则作用:

var reg = /\d/; //包含一个0-9之间的数字
console.log(reg.test('start'));  //false
console.log(reg.test('1'));  // true
console.log(reg.test('start5435'));  //true
var reg = /\d/; //包含一个0-9之间的数字
console.log(reg.exec('start'));  // null
console.log(reg.exec('1'));  // ["1", index: 0, input: "1", groups: undefined]

如何创建正则:

正则的两种创建方式是有区别的,每一个正则表达式都是由元字符和修饰符组成的

//字面量方式:
var reg = /\d/; //包含一个0-9之间的数字
//实例创建方式
var reg = new RegExp('');
  1. 具有特殊意义的元字符
元字符在//之间具有意义的一些字符
\转义字符,转译后面字符所代表的含义
^以某个元字符开始(匹配中是不占位置的)
$以某个元字符结尾(匹配中是不占位置的)
\n匹配一个换行符
.除了\n以外的任意字符
()分组 把一个大正则本身划分成几个小正则
\d一个0-9之间的数字 [0-9]
\D除了0-9之间的数字以外的任何字符
\b匹配一个边界符
\w数字、字母、下划线中的任意一个字符 [0-9a-zA-Z_]
\s匹配一个空白字符,空格、制表符、换页符…
xy
[xyz]x、y、z中的一个
[^xyz]除了三个以外的任何一个字符
[a-z]a-z之间的任何一个字符
[^a-z]除了a-z之间的任何一个字符
  1. 代表出现次数的量词元字符
量词元字符含义
*出现零到多次
+出现一到多次
?出现或者不出现
{n}出现n次
{n,}出现n到多次
{n,m}出现n到m次
var reg = /^\d$/; //只能是一个0-9之间的数字
var reg = /^\d+$/; 
reg.test('12345'); //true
reg.test('1fdgdf2');//false
// .元字符
var reg = /^\d.+\d$/;
reg.test('1fdgdf2'); //true
// () 分组
var reg = /^(\d+)nihao(\d+)$/;
reg.test('1213fdgdf546');//false
reg.test('1213nihao546');//true

注意

正则的应用

  1. 有效数字的正则 正则、负数、零、小数
//"."可以出现也可以不出现,一旦出现,就必须跟一位或多位数字
//最开始可以有+/-也可以没有
//整数部分,一位数可以是0-9之间的一个,多位数不能以0开头
var reg = /^[+-]?(\d|([1-9]\d+))(\.\d+)?$/
  1. 正则的创建方式

在字面量中,//之间包起来的所有字符都会是元字符,有的有特殊意义,大部分都代表本身含义的普通元字符

var name = "attacki";
var reg = /^\d+"+name+"\d+$/;
reg.test('2019attacki2019'); //false
reg.test('2019"""nameeee"2019'); //true
//对于这种正则需要用实例创建方式
var reg = new RegExp("^\\d+"+ name + "\\d+","g");
reg.test('2019attacki2019'); //true

// 字面量方式和实例创建的方式在正则中的区别?
// 字面量方式中出现的一切都是元字符,所以不能进行变量拼接。
// 而实例创建方式是可以的,字面量方式直接写\d就可以,而在实例中需要把它转义 \\d
  1. 年龄介于18-65 18-19 20-59 60-65
var reg = /(1[8-9]|[2-5]\d|6[0-5])/;
  1. 验证邮箱
var reg = /^[\w.-]+@[0-9a-z-A-Z]+(\.[A-Za-z]{2,4}){1,2}$/;
  1. 中国标准真实姓名 2-4位
var reg = /^[\ue400=\u9fa5]{2,4}$/
  1. 身份证号码
var reg = /^\d{17}(\d|X)$/
//正则的捕获
var reg = /^(\d{2})(\d{4})(\d{4})(\d{2})(\d{2})(\d{2})(\d)(\d|X)$/;
  1. 把数字替换为汉字数字
var str = "20190328";
var ary =['','','','','','','','','',''];
str = str.replace(/\d/g,function(){
    return ary[arguments[0]];
})
  1. 获取一个字符串中出现次数最多的字符,并且获取出现次数
var str = "jesus love person, he is the son of god";
var obj ={};
str.replace(/\w|,/gi,function(){
    var val = arguments[0];
    obj[val] >= 1 ? obj[val]++ : obj[val] = 1;
});
//获取最多的次数
var maxNum = 0;
for(var key in obj){
    obj[key] > maxNum ? maxNum = obj[key]:null;
}
//把所有符合出现maxNum次数的都获取到
var ary = [];
for(var key in obj){
    obj[key] === maxNum ? ary.push(key):null;
}
console.log(obj,ary);
  1. 模版引擎实现的初步原理
var str = 'my name is {0} ,my love is {1}';
var ary = ["attacki",'miao'];
str = str.replace(/{(\d+)}/g,function(){
    return ary[arguments[1]];
});
  1. 把字符串中所有单词首字母变为大写

  2. queryUrlParamter

var  str = 'http://kbs.sports.qq.com/game?mid=1000&cid=1454&app=1.0';
var reg =/([^?=&]+)=([^?=&]+)/;
var obj = {};
str.replace(reg,function(){
    obj[arguments[1]] = arguments[2];
})
console.log(obj);
  1. 时间格式化
var str = "2015-5-16 14:53:00";
var reg = /^(\d{4})[-/](\d{1,2})[-/](\d{1,2}) +(\d{1,2}):(\d{1,2}):(\d{1,2})$/;
var ary = [];
str.replace(reg,function(){
    ary = ([].slice.call(arguments)).slice(1,7);
})
//设定好时间格式,把数组中内容替换到指定区域
var resStr = "{0}年{1}月{2}日 {3}时{4}分{5}秒";
str = str.replace(/{(\d+)}/g,function(){
    var val = ary[arguments[1]];
    val.length === 1 ? val='0'+val:null;
    return val;
})
  1. 数据类型检测
typeof		用来检测数据类型的运算符
console.log(typeof 12);
//typeof返回的都是字符串,其中包含对应数据类型
// string number boolean undefined function object
//局限性,不能具体细分是数组、正则还是对象中的其他值。 typeof null === "object"
//使用逻辑或、逻辑与 替代typeof
function(num1,num2){
    if(typeof num2==='undefined'){num2 = 0};
    num2 = num2 || 0;
}
function(callback){
    if(typeof callback==='function'){callback()};
    callback && callback();
}

instanceof 检测某一个实例是否属于某个类

var obj = [];
console.log(obj instanceof Array);
console.log(obj instanceof RegExp);
//1、不能用instanceof 检测基本数据类型的值,因为字面量创建和实例创建的结果分别为false和true。
//对于字面量创建的结果是基本的数据类型,不是严谨的实例,但是由于JS松散特点,可以使用Number.prototype上的方法;
//2、只要在当前实例的原型链上,用instanceof检测的结果都是true

constructor 构造函数,先找私有的,私有没有再找原型

//constructor 和 instanceof非常相似
var obj = [];
console.log(obj.constructor === Array); //true
console.log(obj.constructor === RegExp); //false
//constructor 可以处理基本数据类型
var num = 1;
console.log(num.constructor === Number);
//contsructor检测Object和instanceof不一样,一般情况是检测不了的
var reg = /^$/;
console.log(obj.constructor === RegExp); //true
console.log(obj.constructor === Object); //false
//局限性:类的原型可以进行重写,重写的时候很有可能吧constructor覆盖掉,导致检测结果不准确。

Object.prototype.toString.call()

//Object.prototype.toString他的作用是返回当前方法执行主题(方法中this)所属类的信息
var obj = {name:'miao'};
console.log(obj.toString());
//toString中的this是obj,返回的事obj所属类的信息 ->"[object Object]" 
//第一个object代表当前实例是对象数据类型的(这个是固定死的),第二个Object代表的事obj所属的类是Object
Math.toString();//"[object Math]"

//检测数据类型
var ary = [];
console.log(Object.prototype.toString.call(ary)==="[object Array]");//true
var reg = /^\[object Array\]$/;
console.log(reg.test(Object.prototype.toString.call(ary)));//true

toString的理解

对于NumberStringBooleanArrayRegExpDateFunction原型上的toString方法都是把当前数据类型转化为字符串
Object上的toString不是用来转换字符串的
console.log((1).toString());//Number.prototype.toString转换为字符串 ,可以接受进制参数
console.log((1).__proto__.__proto__.toString());//Object.prototype.toString,[object Object]

exec 正则的捕获

捕获的内容是一个数组。

第一项是当前大正则捕获的内容,
index:捕获内容在字符串中开始的索引位置
input:捕获的原始字符串

正则捕获的特点

> 懒惰性:每次执行exec只捕获第一个匹配的内容,在不进行任何处理的情况下,再执行多次捕获,捕获的还是第一个匹配内容。  
> 正则里面有lastIIndex属性:是正则每次捕获字符串开始查找的位置,默认是0
```js
var reg = /\d+/;
var str = "AE86AE";
var res = reg.exec(str); //["86", index: 2, input: "AE86AE", groups: undefined]
console.log(reg.lastIndex);//  0
res = reg.exec(str); //["86", index: 2, input: "AE86AE", groups: undefined]
console.log(reg.lastIndex);//  0 说明第二次捕获还是从0开始查找的 
```
- 去除正则的懒惰性,可以在正则后面加修饰符

|    修饰符     | g、i、m      |
|:-------------:|:-----------|
|   global(g)   | 全局匹配   |
| ignoreCase(i) | 忽略大小写 |
|  multiple(m)  | 多行匹配   |
```js
var reg = /\d+/g;
var str = "AE8666666AE89AE";
var res = reg.exec(str);
console.log(reg.lastIndex);//  9
res = reg.exec(str);
console.log(reg.lastIndex);//  13
//把所有捕获内容放在数组当中
var str2 = 'AE86AE87AE89';
var ary = [];
var res = reg.exec(str2);
while(res){
    ary.push(res[0]);
    res = reg.exec(str2);
}
console.log(ary);//["86", "87", "89"]
```

- 正则的每一次捕获都是按照匹配最长的结果捕获的。
> 例如:8符合正则,86、87、89也符合,默认捕获的就是86、87、89
- 取消正则的贪婪性,在量词元字符后面加"?"
> ?在正则中有很多作用:  
> 放在普通元字符后面代表出现或者不出现  
> 放在量词元字符后面代表取消捕获的贪婪性  
```js
var reg =/\d+?/g;
//字符串中的match方法 ->把所有符合正则匹配的字符都捕获到
var str2 = 'AE86AE87AE89';
var ary = str2.match(reg); // ["8", "6", "8", "7", "8", "9"]
```

正则分组捕获

- 改变分组优先级
- 分组引用

\1代表和第一个分组出现一模一样;\2代表和第二个分组出现一模一样;

var reg = /(\w)\1(\w)\2/;
console.log(reg.test('aaee')); //true   

分组捕获 正则捕获的时候,可以把大正则和小分组匹配的内容分别捕获

(?:) 在分组中?:的意思是只匹配不捕获

var str = "411325199301270437";
var reg = /^(\d{2})(\d{4})(\d{4})(\d{2})(\d{2})(\d{2})(\d)(?:\d|X)$/;
var ary = reg.exec(str)
//ary[0] ->大正则匹配的内容
//ary[1] ->第一个分组匹配的内容
console.log(str.match(reg));//和exec捕获的内容是一样的

reg = /AE(\d+)/g;
str = "AE86AE87AE89";//match只能捕获大正则的内容

replace:把原有的字符替换为新字符

在不使用正则的情况下,每当执行一次只能替换一次

var str = "lovejesus2018lovejesus2019";
str = str.replace("love","lovejesus").replace("love","lovejesus"); 
//"lovejesusjesusjesus2018lovejesus2019"
str = str.replace(/love/g,"lovejesus"); 
//"lovejesusjesus2018lovejesusjesus2019"
//replace方法的第二个参数可以是一个函数
var str = "love2018love2019";
str = str.replace(/love/g,function(){
    console.log(arguments);
    return "lovejesus";
})
//第二个参数换成一个函数
//1)匿名函数执行多少次,取决于正则捕获的次数
//2)每一次执行匿名函数,里面得到的argumens和exec方法捕获的内容非常相似
//(即使正则有分组,同样可以通过arguments获取到分组捕获的内容)
//3)把大正则捕获的内容替换为return的内容


Previous Post
regexp小例子
Next Post
脚手架运行原理