ES6之 字符串和正则表达式

字符串和正则表达式

ES6 为我们增添几个常用的字符串操作方法

  • includes() 方法。检测指定文本,匹配到结果返回true,否则为false。可以用来替换indexOf来判断字符串是否存在于另一个字符串中。

    • "abc".indexOf("a") > -1 => "abc".includes("a")

    • 当需要获取字段的索引值时,只能依靠indexOf(或lastIndexOf),匹配到的索引值为字符串第一个字符在 另一个字符串中的位置。"abcd".indexOf("bc") //1

  • startsWith() 方法。在字符串的开始部分是否是以指定文本,是则返回true,反之为false。

  • endsWith() 方法。在字符串的结尾部分是否是以指定文本,是则返回true,反之为false。

    • 上面两个方法均可接受两个参数。以startsWith为例。

    • "abc".startsWith('b', 1) // true

      • 第一个参数为匹配文本

      • 第二个参数设置检测的起始位置

      • endsWith中,第二个参数设置起始位置为 字符长度 - 参数值

  • repeat() 方法。接收一个number类型参数,表示改字符串的重复次数。

    • "ab".repeat(2) // abab

    • 若接收字符串可转为数字。则进行隐式转换。

    • 若接收字符串不可转为数字。则返回空。


新增3个字符编码的方法。

JavaScript的字符编码方式为 UTF-16 进行构建,即用 16个 0 / 1的组合表示一个字符。
  • 编码单元:这16个 0或1 的组合称为一个编码单元。

  • 基本多文种平面:编码单元的取值范围称为基本多文中平面。

    • 基本多文种平面 -> Basic Multilingual Plane. (以下简称:BMP)
对于超出BMP的字符,16位编码就无法表示了。为此UTF-16引入了代理对,用两个16位编码单元表示一个字符。
  • codePointAt() // 将字符转换为字符编码。

    • 对于BMP字符集中的字符,与charCodeAt()效果相同。

       "a".charCodeAt() // 97 
       "a".codePointAt() // 97
      
    • 对于非BMP字符集中的字符,会展示完整的字符编码。

      •  "𠮷".charCodeAt()   // 55362
         "𠮷".charCodeAt(0)  // 55362  
         "𠮷".charCodeAt(1)  // 57271 
        
         "𠮷".codePointAt()  // 134071 
         "𠮷".codePointAt(0) // 134071 
         "𠮷".codePointAt(1) // 57271 
         "𠮷".codePointAt(2) // undefined  
        
      • 根据以上的代码,我们可以知道,ES 5 中的charCodeAt()方法,对于非BMP字符集中的字符,只截取代理对中的第一段16位编码,第二段则默认忽略。

      • 而ES 6新增的codePointAt()方法,很合理的使用了整个代理对来进行解析。且代理对的第二段作为辅助平面,而不做单独解析。

    • 关于检测一个字符占用的编码单元数量,以下函数可提供参考。

        function is32Bit(c) {     
           return c.codePointAt(0) > 0xFFFF;  
        }  
        console.log(is32Bit("𠮷")) // true  
        console.log(is32Bit("a"))  // false
      
  • String.fromCodePoint() // 将字符编码转换为字符

    • 对于BMP字符集中的字符,与fromCharCode()效果相同。

      • String.fromCharCode(97) // a

      • String.fromCodePoint(97) // a

    • 对于非BMP字符集中的字符,fromCodePoint能展示更完整的字符。

      • String.fromCharCode(9731, 9733, 9842, 0x2F804) // ☃★♲

      • String.fromCodePoint(9731, 9733, 9842, 0x2F804) // ☃★♲你

  • normalize() // 统一标准化编码的表示形式,通常在国际化应用中使用较多

    • 有很多欧洲国家的语言中有语调和重音符号。Unicode提供了两种方法来进行编码。

      • 一种是直接带重音符号的字符 例如:Ǒ (\u01D1)

      • 另一种是合成符号:O (\u030C) 和 ̌(\u030C) 组成 Ǒ (\u01D1)

    • 两种方式变现的字符相同,但字符并不相同 '\u01D1'==='\u004F\u030C' //false。ES 6新增的normalize(),能将Unicode正规化。可传入四个可选值。

      • NFC。默认参数,按照标准等价分解,然后在以标准等价方式进行重组。

      • NFD。按照标准等价分解。

      • NFD。按照兼容等价分解。

      • NFKD。按照兼容等价分解,然后在以兼容等价方式进行重组。

      • 标准等价:视觉展示和语义上等价。

      • 兼容等价:语义等价、视觉展示不等价。

    • 在进行排序时,建议统一标准化进行处理。以下例子可供参考:

    • arr.sort(function(first, second) {   
           let firstNormalized = first.normalize(),        
                secondNormalized = second.normalize();    
           if (firstNormalized < secondNormalized) {        
               return -1;     
           } else if (firstNormalized === secondNormalized) {        
               return 0;    
           } else {        
               return 1;    
           }
       });
      

正则表达式的u修饰符

JavaScript的正则默认会将字符进行16位编码处理,某些单个字符解析成两个编码。例如:"𠮷"
    let text = "𠮷";
    console.log(/^.$/.test(text));  // 匹配一个字符时得到 false  

ES 6加如了 u修饰符,使编码操作处理变为按字符处理。

    let text = "𠮷";
    console.log(/^.$/.test(text)); // 匹配一个字符时得到 false
    console.log(/^.$/u.test(text)); // 匹配一个字符时得到 true
判断浏览器JS解释器是否支持正则中的 u修饰符,以下函数可提供参考:
  function hasExpU () {      
    try {          
      var pattern = new RegExp(",", u);          
         return true;      
    } catch (ex) { 
         return false;      
    }        
  }

正则表达式的y修饰符

正则采用y修饰符后,匹配将会从lastIndex属性值的位置为起点开始匹配。若从lastIndex开始的字符匹配到结果,则返回true,否则为false。换种说法,以lastIndex值为起点,进行startsWith匹配。
    let text = "hello1 hello2 hello3",    
    pattern = /hello\d/y;    
    console.log(pattern.test(text)); // true
    pattern.lastIndex = 1;     // 初始化匹配起始位置
    console.log(pattern.test(text)); // false 
  • lastIndex = 1,即从"ello1 hello2 hello3"中匹配。正则中是以h开头,与e不同,所以返回false。

  • 当y修饰符的正则匹配到结果后,lastIndex会更新为匹配到结果的最后一个索引值。再次正则匹配操作,会进行叠加,称为粘滞行为。

  let text = "hello1 hello2 hello3",    
       pattern = /hello\d\s/y;    
  console.log(pattern.test(text));  // true
  console.log(pattern.test(text));  // true
  console.log(pattern.test(text));  // false (因为hello3后面无空格)
  • 注意:只有调用正则对象的方法才会涉及粘滞行为。test、exec等。而字符串的方法不会触发,如match。
正则表达式的复制
  • 通过给RegExp构造函数传递第二个参数,可进行修饰符的覆盖。
    let re1 = /abc/i,      
         re2 = new RegExp(re1, "g");        
    console.log(re2);  //   /abc/g 
正则的其他
  • flags 可获取正则表达式的修饰符。
  let re1 = /abc/img;    
  console.log(re1.source);  // abc   (ES5 获取正则表达式文本)  
  console.log(re1.flags);   // img   (ES6 获取正则表达式修饰符)

模版字面量

模版字面量使用一对反引号表示( `` ),具有以下特点:
  • 支持字符串拼接

  • 支持换行

  • 支持空格记录 (换行情况下)

  • 支持占位符 ( ${}, 且占位符中可以正常执行函数,表达式)

  • 标签函数

对于ES 6之前的字符串拼接, '+' 和 '' 虽然能够完成拼接,但变量插入,换行、空格记录等十分繁琐。ES 6 提供的模版字面量提高了这一方面的开发效率。
  let text = "test",    
       str = `This is     
       ${text} sentence!` ;
  console.log(str); 
  // This is    
      test sentence!
标签函数:对模版字面量进一步处理的函数,放在反引号之前。
  • 标签函数接收两个参数,literals 和 ...substitutions。

  • 第一个参数是一个数组。以占位符为分割符号,分割元素组成的数组,累死split()函数分割后的返回值。

  • 第二个参数为 占位符的集合,是一种类数组结构,有length属性

  let message = tag`hello world`; // tag 是我们自定义的一个标签模版函数   
  function tag(literals, ...substitutions) {      
    // 内容  
  }

用法:模版处理、规避恶意脚本

  • 模版处理。先看下面的例子,例子中str变量正确的获取了字符串。
  let a = 1, b = 2,      
       str = `${a} + ${b} = ${a + b}`;  
  console.log(str); // 1 + 2 = 3  

现在需求变成 '10 + 20 = 30',模版变成:

   str = `${a * 10} + ${b * 10} = ${a * 10 + b * 10}`; // 10 + 20 = 30

不免有些麻烦,模版较长时,维护起来并不容易。使用标签函数试试。

  const a = 1, b = 2; 
  const str = strWork`${a} + ${b} = ${a + b}`;  
 
  function strWork(literals, ...substitutions) {   
    let result = '';   
    for (let i = 0; i < substitutions.length; i++) {     
      result += literals[i];
      result += String(Number(substitutions[i]) * 10);    
    }   
    return result; 
  } 

  console.log(str); // 10 + 20 = 30
  • 规避恶意脚本
  // 模板标签过滤HTML字符串    
  function safeHTML(templateData) {      
    let s = templateData[0];      
    const args = arguments;      
    for (let i = 1, len = args.length; i < len; i++) {       
      let arg = String(args[i]);        // 特殊字符的替换        
      s += arg.replace(/&/g, "&").replace(//g, ">");        
      s += templateData[i];      
    }      
    return s;     
  }     
  const str = ' <&&&&';            
  console.log(safeHTML`hello world ${str}`);     // hello world <p> <&&&&

参考书籍:

《深入理解ES6》[美] NICHOLAS C. ZAKAS 著 刘振涛 译

posted @ 2019-11-14 11:43  miku561  阅读(3035)  评论(0编辑  收藏  举报