正则表达式
正则表达式最常用于处理和校验字符串,是非常好用的简便工具。如果用的熟练,能够极大的提高工作效率。并且在阅读前端框架源码的时候经常也会遇到正则,记录在此以备忘。
基础的正则表达式示例
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. 多选分支
- (p1|p2|p3) ,其中 p1、p2 和 p3 是子模式,用 |(管道符)分隔,表示其中任何之一。
分支结构是惰性的,即当前面的匹配上了,后面的就不再尝试了。
例子汇总一:
// 匹配 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
- 捕获:把字符串中符合我们正则规则的内容捕获到 -> exec:reg.exec(str)
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('');
- 具有特殊意义的元字符
元字符 | 在//之间具有意义的一些字符 |
---|---|
\ | 转义字符,转译后面字符所代表的含义 |
^ | 以某个元字符开始(匹配中是不占位置的) |
$ | 以某个元字符结尾(匹配中是不占位置的) |
\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之间的任何一个字符 |
- 代表出现次数的量词元字符
量词元字符 | 含义 |
---|---|
* | 出现零到多次 |
+ | 出现一到多次 |
? | 出现或者不出现 |
{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
注意
-
[]
- 在中括号中出现的所有字符都是代表本身意思的字符
- 中括号中不识别二位数
var reg = /^[12-68]$/; //1、2-6或者8中的一个 var reg = /^[\w-]]$/; //数字、字母、下划线、-中的一个
-
()
- 改变x|y的默认优先级
- /a+/ 表示连续出现 a ,而需要连续出现 ab,则需要 /(ab)+/
- 在多选分支结构(p1|p2)中,括号的作用是提供了子表达式的所有可能
- 一般情况下分组的作用是为了引用或者重复出现分组或者分支结构
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
正则的应用
- 有效数字的正则 正则、负数、零、小数
//"."可以出现也可以不出现,一旦出现,就必须跟一位或多位数字
//最开始可以有+/-也可以没有
//整数部分,一位数可以是0-9之间的一个,多位数不能以0开头
var reg = /^[+-]?(\d|([1-9]\d+))(\.\d+)?$/
- 正则的创建方式
在字面量中,//之间包起来的所有字符都会是元字符,有的有特殊意义,大部分都代表本身含义的普通元字符
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
- 年龄介于18-65 18-19 20-59 60-65
var reg = /(1[8-9]|[2-5]\d|6[0-5])/;
- 验证邮箱
var reg = /^[\w.-]+@[0-9a-z-A-Z]+(\.[A-Za-z]{2,4}){1,2}$/;
- 中国标准真实姓名 2-4位
var reg = /^[\ue400=\u9fa5]{2,4}$/
- 身份证号码
var reg = /^\d{17}(\d|X)$/
//正则的捕获
var reg = /^(\d{2})(\d{4})(\d{4})(\d{2})(\d{2})(\d{2})(\d)(\d|X)$/;
- 把数字替换为汉字数字
var str = "20190328";
var ary =['零','一','二','三','四','五','六','七','八','九'];
str = str.replace(/\d/g,function(){
return ary[arguments[0]];
})
- 获取一个字符串中出现次数最多的字符,并且获取出现次数
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);
- 模版引擎实现的初步原理
var str = 'my name is {0} ,my love is {1}';
var ary = ["attacki",'miao'];
str = str.replace(/{(\d+)}/g,function(){
return ary[arguments[1]];
});
-
把字符串中所有单词首字母变为大写
-
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);
- 时间格式化
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;
})
- 数据类型检测
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的理解
对于Number、String、Boolean、Array、RegExp、Date、Function原型上的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
```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的内容