强大的正则表达式

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

1
2
3
4
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的修饰下才会有的。
在量词后面追加一个?,就可使贪婪变为懒惰

1
2
3
4
5
6
7
8
9
10
11
12
13
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]

正则实用实例

1
2
3
4
5
// 匹配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. 两种模糊匹配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 横向模糊

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. 字符组

1
2
3
// [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. 多选分支

  • (p1|p2|p3) ,其中 p1、p2 和 p3 是子模式,用 |(管道符)分隔,表示其中任何之一。

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

例子汇总一:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 匹配 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后面的位置

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

例子汇总二:

1
2
3
4
5
6
7
8
9
10
11
// 不匹配任何东西
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}$/

三、圆括号的作用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// 分组
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
//反向引用 \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

非捕获括号

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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)相匹配的文本内容
*/

例子汇总三:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 字符串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()
})
}

四、回溯

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

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

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

五、正则表达式的拆分

1. 结构和操作符

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

2. 匹配字符串整体问题

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

1
var reg = /^abc|bcd$/

3. 量词连缀问题

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

4. 元字符转义问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 所有结构里,用到的元字符总结如下:
// ^ $ . * + ? | \ / ( ) [ ] { } = ! : -
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. 正则的四种操作

验证

1
2
3
4
5
6
7
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

切分

1
2
3
4
5
6
7
8
9
10
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"]

提取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 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"]

替换

1
2
3
4
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,会把字符串转换为正则的。

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

3. match 返回结果的格式

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

1
2
3
4
5
6
7
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 个字符处开始尝试匹配。

1
2
3
4
5
6
7
8
9
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 整体匹配时需要使用 ^ 和 $

1
2
3
4
5
6
console.log( /123/.test("a123b") );
// => true
console.log( /^123$/.test("a123b") );
// => false
console.log( /^123$/.test("123") );
// => true

6. split 相关注意事项

1
2
3
4
5
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 个 分组里捕获的文本
$& 匹配到的子串文本
$` 匹配到的子串的左边文本
$’ 匹配到的子串的右边文本
$$ 美元符号$$

1
2
3
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。

1
2
3
4
5
6
7
var regex = /\w/img;
console.log( regex.global );
console.log( regex.ignoreCase );
console.log( regex.multiline );
// => true
// => true
// => true

正则作用:

  • 正则:用来处理字符串的规则

  • 匹配:判断一个字符串是否符合我们制定的规则

    1
    2
    3
    4
    var reg = /\d/; //包含一个0-9之间的数字
    console.log(reg.test('start')); //false
    console.log(reg.test('1')); // true
    console.log(reg.test('start5435')); //true
  • 捕获:把字符串中符合我们正则规则的内容捕获到 -> exec:reg.exec(str)

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

如何创建正则:

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

1
2
3
4
//字面量方式:
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 匹配一个空白字符,空格、制表符、换页符…
x y
[xyz] x、y、z中的一个
[^xyz] 除了三个以外的任何一个字符
[a-z] a-z之间的任何一个字符
[^a-z] 除了a-z之间的任何一个字符
  1. 代表出现次数的量词元字符
量词元字符 含义
* 出现零到多次
+ 出现一到多次
? 出现或者不出现
{n} 出现n次
{n,} 出现n到多次
{n,m} 出现n到m次
1
2
3
4
5
6
7
8
9
10
11
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. 在中括号中出现的所有字符都是代表本身意思的字符
    2. 中括号中不识别二位数
      1
      2
      var reg = /^[12-68]$/; //1、2-6或者8中的一个
      var reg = /^[\w-]]$/; //数字、字母、下划线、-中的一个
  • ()

    1. 改变x|y的默认优先级
    2. /a+/ 表示连续出现 a ,而需要连续出现 ab,则需要 /(ab)+/
    3. 在多选分支结构(p1|p2)中,括号的作用是提供了子表达式的所有可能
    4. 一般情况下分组的作用是为了引用或者重复出现分组或者分支结构
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      var reg = /^i love (javascript|regular expression)$/
      console.log(reg.test('i love javascript'));
      console.log(reg.test('i love regular expression'));
      // true
      // true
      reg = /(\d{4})-(\d{2})-(\d{2})/
      var reg = /^18|19$/;
      //18、19、181、1819、119、18
      var reg = /^(18|19)$/;
      //18、19x

正则的应用

  1. 有效数字的正则 正则、负数、零、小数

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    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
  3. 年龄介于18-65 18-19 20-59 60-65

    1
    var reg = /(1[8-9]|[2-5]\d|6[0-5])/;
  4. 验证邮箱

    1
    var reg = /^[\w.-]+@[0-9a-z-A-Z]+(\.[A-Za-z]{2,4}){1,2}$/;
  5. 中国标准真实姓名 2-4位

    1
    var reg = /^[\ue400=\u9fa5]{2,4}$/
  6. 身份证号码

    1
    2
    3
    var reg = /^\d{17}(\d|X)$/
    //正则的捕获
    var reg = /^(\d{2})(\d{4})(\d{4})(\d{2})(\d{2})(\d{2})(\d)(\d|X)$/;
  7. 把数字替换为汉字数字

    1
    2
    3
    4
    5
    var str = "20190328";
    var ary =['零','一','二','三','四','五','六','七','八','九'];
    str = str.replace(/\d/g,function(){
    return ary[arguments[0]];
    })
  8. 获取一个字符串中出现次数最多的字符,并且获取出现次数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    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);
  9. 模版引擎实现的初步原理

    1
    2
    3
    4
    5
    var str = 'my name is {0} ,my love is {1}';
    var ary = ["attacki",'miao'];
    str = str.replace(/{(\d+)}/g,function(){
    return ary[arguments[1]];
    });
  10. 把字符串中所有单词首字母变为大写

  11. queryUrlParamter

    1
    2
    3
    4
    5
    6
    7
    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);
  12. 时间格式化

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    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;
    })
  13. 数据类型检测

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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 检测某一个实例是否属于某个类

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

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

1
2
3
4
5
6
7
8
9
10
11
12
//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()

1
2
3
4
5
6
7
8
9
10
11
12
//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的理解

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

exec 正则的捕获

  • 捕获的内容格式,匹配成功才有内容,匹配失败结果为null

    捕获的内容是一个数组。

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

    正则捕获的特点

    懒惰性:每次执行exec只捕获第一个匹配的内容,在不进行任何处理的情况下,再执行多次捕获,捕获的还是第一个匹配内容。
    正则里面有lastIIndex属性:是正则每次捕获字符串开始查找的位置,默认是0

    1
    2
    3
    4
    5
    6
    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) 多行匹配
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      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

    • 取消正则的贪婪性,在量词元字符后面加”?”

      ?在正则中有很多作用:
      放在普通元字符后面代表出现或者不出现
      放在量词元字符后面代表取消捕获的贪婪性

      1
      2
      3
      4
      var reg =/\d+?/g;
      //字符串中的match方法 ->把所有符合正则匹配的字符都捕获到
      var str2 = 'AE86AE87AE89';
      var ary = str2.match(reg); // ["8", "6", "8", "7", "8", "9"]

正则分组捕获

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

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

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

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

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

1
2
3
4
5
6
7
8
9
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:把原有的字符替换为新字符

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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的内容