ES6学习笔记——RegExp的拓展

作者: island_0d48 | 来源:发表于2017-09-01 01:02 被阅读154次

    正则的拓展

    在ES5中,我们声明正则表达式一般有两种方法

    • 参数是字符串,这时第二个参数表示正则表达式的修饰符
    • 参数是一个正则表达式,这时会返回一个原有正则表达式的拷贝

    但是,不允许这种情况

    var regex = new RegExp(/xyz/, 'i');
    

    ES6改变了这个状况,如果RegExp构造函数第一个参数是一个正则对象,那么可以使用第二个参数指定修饰符。而且,返回的正则表达式会忽略原有的正则表达式的修饰符,只使用新指定的修饰符。

    new RegExp(/abc/ig, 'i').flags
    // "i"
    // 原有正则对象的修饰符是ig,它会被第二个参数i覆盖
    

    字符串的正则方法

    字符串自身就可以调用正则方法,比如match(), replace(), search(), split()。

    ES6干脆把这几个方法都封装在了RexExp上。

    • String.prototype.match
    • String.prototype.replace
    • String.prototype.search
    • String.prototype.split

    u

    ES6中添加了u修饰符,来处理大于\uFFFF的unicode字符。

    /^\uD83D/u.test('\uD83D\uDC2A') // false
    /^\uD83D/.test('\uD83D\uDC2A') // true
    

    上面代码中,\uD83D\uDC2A是一个四个字节的 UTF-16 编码,代表一个字符。但是,ES5 不支持四个字节的 UTF-16 编码,会将其识别为两个字符,导致第二行代码结果为true。加了u修饰符以后,ES6 就会识别其为一个字符,所以第一行代码结果为false。

    u修饰符会影响以下行为:

    点字符

    var s = '𠮷';
    
    /^.$/.test(s) // false
    /^.$/u.test(s) // true
    

    unicode字符表示法

    /\u{61}/.test('a') // false
    /\u{61}/u.test('a') // true
    /\u{20BB7}/u.test('𠮷') // true
    

    量词

    /a{2}/.test('aa') // true
    /a{2}/u.test('aa') // true
    /𠮷{2}/.test('𠮷𠮷') // false
    /𠮷{2}/u.test('𠮷𠮷') // true
    

    预定义模式

    /^\S$/.test('𠮷') // false
    /^\S$/u.test('𠮷') // true
    

    i修饰符

    //有些 Unicode 字符的编码不同,但是字型很相近,比如,\u004B与\u212A都是大写的K。
    
    /[a-z]/i.test('\u212A') // false
    /[a-z]/iu.test('\u212A') // true
    //上面代码中,不加u修饰符,就无法识别非规范的K字符。
    

    y修饰符

    ES6添加了一个叫做“粘连”的y修饰符。y和g的作用其实是差不多的,但是g是在上次匹配成功的后面的串种只有有匹配的串就行了,但是y必须是从上次匹配成功的地方开始。

    var s = 'aaa_aa_a';
    var r1 = /a+/g;
    var r2 = /a+/y;
    
    r1.exec(s) // ["aaa"]
    r2.exec(s) // ["aaa"]
    
    r1.exec(s) // ["aa"]
    r2.exec(s) // null
    

    同时,ES6新增了一个sticky属性看是不是设置了y,并设置了一个flags属性来返回修饰符

    s修饰符:dotAll

    ES6引入s修饰符使得.可以通配所有的字符。并且设置了一个dotAll属性看是不是使用了这个属性。

    后行断言

    ES本来只有先行断言和先行否定断言,但是ES6引入了后行断言和后行否定断言。

    • 先行断言:x只有在y前面才匹配,必须写成/x(?=y)/。比如,只匹配百分号之前的数字,要写成/\d+(?=%)/
    • 先行否定断言:x只有不在y前面才匹配,必须写成/x(?!y)/。比如,只匹配不在百分号之前的数字,要写成/\d+(?!%)/
    • 后行断言:x只有在y后面才匹配,必须写成/(?<=y)x/。比如,只匹配美元符号之后的数字,要写成/(?<=$)\d+/
    • 后行否定断言:x只有不在y后面才匹配,必须写成/(?<!y)x/。比如,只匹配不在美元符号后面的数字,要写成/(?<!$)\d+/

    Unicode属性类

    这个新加的特性非常猛,可以通过\p来匹配unicode属性相关的东西。比如:

    const regexGreekSymbol = /\p{Script=Greek}/u;
    regexGreekSymbol.test('π') // true
    

    上面代码匹配了一个希腊字母。

    规则:

    \p{UnicodePropertyName=UnicodePropertyValue}

    有些属性可以致谢属性名。

    注意,这两种类只对 Unicode 有效,所以使用的时候一定要加上u修饰符。如果不加u修饰符,正则表达式使用\p和\P会报错,ECMAScript 预留了这两个类。

    功能非常强:

    const regex = /^\p{Decimal_Number}+$/u;
    regex.test('𝟏𝟐𝟑𝟜𝟝𝟞𝟩𝟪𝟫𝟬𝟭𝟮𝟯𝟺𝟻𝟼') // true
    
    // 匹配所有数字
    const regex = /^\p{Number}+$/u;
    regex.test('²³¹¼½¾') // true
    regex.test('㉛㉜㉝') // true
    regex.test('ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫ') // true
    
    // 匹配各种文字的所有字母,等同于 Unicode 版的 \w
    [\p{Alphabetic}\p{Mark}\p{Decimal_Number}\p{Connector_Punctuation}\p{Join_Control}]
    
    // 匹配各种文字的所有非字母的字符,等同于 Unicode 版的 \W
    [^\p{Alphabetic}\p{Mark}\p{Decimal_Number}\p{Connector_Punctuation}\p{Join_Control}]
    
    // 匹配所有的箭头字符
    const regexArrows = /^\p{Block=Arrows}+$/u;
    regexArrows.test('←↑→↓↔↕↖↗↘↙⇏⇐⇑⇒⇓⇔⇕⇖⇗⇘⇙⇧⇩') // true
    

    具名组匹配

    原本的组匹配就是加一个圆括号,然后就可以用序号访问到。

    const RE_DATE = /(\d{4})-(\d{2})-(\d{2})/;
    
    const matchObj = RE_DATE.exec('1999-12-31');
    const year = matchObj[1]; // 1999
    const month = matchObj[2]; // 12
    const day = matchObj[3]; // 31
    

    现在引入了具体的名称,便于引用。

    const RE_DATE = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
    
    const matchObj = RE_DATE.exec('1999-12-31');
    const year = matchObj.groups.year; // 1999
    const month = matchObj.groups.month; // 12
    const day = matchObj.groups.day; // 31
    

    在圆括号内部,模式的头部添加“问号 + 尖括号 + 组名”(?<year>),然后就可以在exec方法返回结果的groups属性上引用该组名。同时,数字序号(matchObj[1])依然有效。

    用法非常骚气:

    // 可以直接赋值
    let {groups: {one, two}} = /^(?<one>.*):(?<two>.*)$/u.exec('foo:bar');
    one  // foo
    two  // bar
    
    // 直接replace
    let re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u;
    
    '2015-01-02'.replace(re, '$<day>/$<month>/$<year>')
    // '02/01/2015'
    
    // 内部自己引用
    const RE_TWICE = /^(?<word>[a-z]+)!\k<word>$/;
    RE_TWICE.test('abc!abc') // true
    RE_TWICE.test('abc!ab') // false
    

    相关文章

      网友评论

        本文标题:ES6学习笔记——RegExp的拓展

        本文链接:https://www.haomeiwen.com/subject/dkchjxtx.html