前言
一直不是很理解正则中的(?=p)、(?!p)、(?<=p)、(?<!p),按照官方的介绍晦涩难懂
比如:(?:pattern):匹配pattern但不获取匹配结果,也就是说这是一个非获取匹配,不进行存储供以后使用
刚看到这句话的我..

这也太难懂了吧。
另一种解释
// 前瞻:
exp1(?=exp2) 查找exp2前面的exp1
// 后顾:
(?<=exp2)exp1 查找exp2后面的exp1
// 负前瞻:
exp1(?!exp2) 查找后面不是exp2的exp1
// 负后顾:
(?<!exp2)exp1 查找前面不是exp2的exp1
//举例
"中国人".replace(/(?<=中国)人/, "rr") // 匹配中国人中的人,将其替换为rr,结果为 中国rr
"法国人".replace(/(?<=中国)人/, "rr") // 结果为 法国人,因为人前面不是中国,所以无法匹配到
这种理解方式对于简单的例子还能理解,但是如果遇到 /(?!\b)(?=(\d{3})+\b)/g 这种表达式呢, (?!exp2)前面没有表达式怎么破?
正文
要理解(?=p)跟(?!p),要知道正则里面有个 匹配位置 这一说。
正则的匹配模式分为两种
- 匹配字符
- 匹配位置
匹配字符
匹配字符我相信都能理解,直接匹配字符串中的字符
var regex = /hello/;
console.log( regex.test("hello") );
// => true
匹配字符还分为纵向匹配,以及横向匹配
//横向匹配,一个正则可匹配的字符串的长度不是固定的
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"]
好了,匹配字符就介绍到这里,这不是本文的重点,重点是匹配位置
匹配位置
什么是位置
位置是相邻字符之间的位置,可以理解成空字符 ""。比如,下图中箭头所指的地方

"hello" == "" + "h" + "" + "e" + "" + "l" + "" + "l" + "" + "o" + ""
(?=p)、(?!p)
(?=p)匹配的是什么呢,匹配的是 p 前面的位置,或者说,该位置后面的字符要匹配 p。
比如 (?=h),表示 "h" 字符前面的位置,例如:
var result = "hello".replace(/(?=h)/g, '#');
console.log(result);
// => "#hello"
而 (?!p) 就是 (?=p) 的反面意思,比如:
var result = "hello".replace(/(?!h)/g, '#');
console.log(result);
// => "h#e#l#l#o#" 除了h前面没有加#,其他的位置都加上了#
正则
现在再回过头来看下那个正则:var regex = /(?!^)(?=(\d{3})+$)/g
它是用来将数字用千位分隔符表示,比如把 "12345678",变成 "12,345,678
var regex = /(?!^)(?=(\d{3})+$)/g;
var result = "12345678".replace(regex, ',')
console.log(result);
// => "12,345,678"
result = "123456789".replace(regex, ',');
console.log(result);
// => "123,456,789"
分析
正则 /(?!^)(?=(\d{3})+$) 分为 (?!^) 以及 (?=(\d{3})+$)
(?=(\d{3})+$) :(?=\d{3}$) 匹配 \d{3}$ 前面的位置。而 \d{3}$ 匹配的是目标字符串最后那 3 位数字
var result = "12345678".replace(/(?=\d{3}$)/g, ',')
console.log(result);
// => "123456,789"
因为逗号出现的位置,要求后面 3 个数字一组,也就是 \d{3} 至少出现一次。此时可以使用量词 +:
var result = "12345678".replace(/(?=(\d{3})+$)/g, ',')
console.log(result);
// => ",123,456,789"
(?!^) :匹配前面不是开头的位置,如果不加上的话,会 "123456789",变成 ",123,456,789",开头会有一个逗号
(?<=p)、(?<!p)
说到这里,(?<=p)跟(?<!p)也就不难理解了
//(?<=p)表示p表达式后面的位置
var result = "hello".replace(/(?<=h)/g, '#');
// => "h#ello"
而 (?!p) 就是 (?=p) 的反面意思,比如:
//(?<!p)是(?<=p)的反面意思,匹配不是p表达式后面的位置
var result = "hello".replace(/(?<!h)/g, '#');
// => "#he#l#l#o#"