ECMAScript 6(二)

字符串的扩展

Posted by     "Jordon Li" on Wednesday, December 18, 2019

TOC

ECMAScript 6 简介

ECMAScript 6.0(以下简称 ES6)是 JavaScript 语言的下一代标准,已经在 2015 年 6 月正式发布了。它的目标,是使得 JavaScript 语言可以用来编写复杂的大型应用程序,成为企业级开发语言。

字符串的扩展

字符的 Unicode 表示法

  • 基本用法: ES6 加强了对Unicode的支持,允许采用\uxxxx形式表示一个字符,其中xxxx表示字符的Unicode码点。 但是,这种表示法只限于码点在\u0000~\uFFFF之间的字符。超出这个范围的字符,必须用两个双字节的形式表示。 ES6 对这一点做出了改进,只要将码点放入大括号,就能正确解读该字符。有了这种表示法之后,JavaScript 共有 6 种方法可以表示一个字符:
    '\z' === 'z'  // true
    '\172' === 'z' // true
    '\x7A' === 'z' // true
    '\u007A' === 'z' // true
    '\u{7A}' === 'z' // true
    

字符串的遍历器接口

  • 基本用法: ES6 为字符串添加了遍历器接口Iterator,使得字符串可以被for...of循环遍历。
    for(let codePoint of 'foo')
    {
      console.log(codePoint)
    }
    // "f"
    // "o"
    // "o"
    
    //除了遍历字符串,这个遍历器最大的优点是可以识别大于0xFFFF的码点,传统的for循环无法识别这样的码点。
    let text = String.fromCodePoint(0x20BB7);
    for (let i of text) {
      console.log(i);
    }
    // "𠮷"
    

直接输入 U+2028 和 U+2029

  • 基本用法: JavaScript 字符串允许直接输入字符,以及输入字符的转义形式。但是,JavaScript 规定有5个字符,不能在字符串里面直接使用,只能使用转义形式。
    U+005C:反斜杠(reverse solidus)
    U+000D:回车(carriage return)
    U+2028:行分隔符(line separator)
    U+2029:段分隔符(paragraph separator)
    U+000A:换行符(line feed)
    

    这个规定本身没有问题,麻烦在于 JSON 格式允许字符串里面直接使用U+2028(行分隔符)和U+2029(段分隔符)。这样一来,服务器输出的 JSON 被JSON.parse解析,就有可能直接报错。

    const json = '"\u2028"';
    JSON.parse(json); // 可能报错
    

    JSON 格式已经冻结(RFC 7159),没法修改了。为了消除这个报错,ES2019 允许 JavaScript 字符串直接输入 U+2028(行分隔符)和 U+2029(段分隔符)。

    const PS = eval("'\u2029'");
    

    注意,模板字符串现在就允许直接输入这两个字符。另外,正则表达式依然不允许直接输入这两个字符,这是没有问题的,因为 JSON 本来就不允许直接包含正则表达式。

JSON.stringify() 的改造

  • 基本用法: 根据标准,JSON 数据必须是 UTF-8 编码。但是,现在的JSON.stringify()方法有可能返回不符合 UTF-8 标准的字符串。 具体来说,UTF-8 标准规定,0xD8000xDFFF之间的码点,不能单独使用,必须配对使用。JSON.stringify()的问题在于,它可能返回0xD8000xDFFF之间的单个码点。 为了确保返回的是合法的 UTF-8 字符,ES2019 改变了JSON.stringify()的行为。如果遇到0xD8000xDFFF之间的单个码点,或者不存在的配对形式,它会返回转义字符串,留给应用自己决定下一步的处理。
    JSON.stringify('\u{D834}') // ""\\uD834""
    JSON.stringify('\uDF06\uD834') // ""\\udf06\\ud834""
    

模板字符串

  • 基本用法: 模板字符串(template string)是增强版的字符串,用反引号(`)标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。
    // 普通字符串
    `In JavaScript '\n' is a line-feed.`
    
    // 多行字符串
    `In JavaScript this is
    not legal.`
    
    console.log(`string text line 1
    string text line 2`);
    
    // 字符串中嵌入变量
    let name = "Bob", time = "today";
    `Hello ${name}, how are you ${time}?`
    

标签模板

  • 基本用法: 模板字符串的功能不仅仅是上面这些,它可以紧跟在一个函数名后面,该函数将被调用来处理这个模板字符串。这被称为标签模板功能(tagged template)。
    alert`123`
    // 等同于
    alert(123)
    
    let a = 5;
    let b = 10;
    tag`Hello ${ a + b } world ${ a * b }`;
    // 等同于
    tag(['Hello ', ' world ', ''], 15, 50);
    

字符串的新增方法

String.fromCodePoint()

  • 基本用法: ES5 提供String.fromCharCode()方法,用于从 Unicode 码点返回对应字符,但是这个方法不能识别码点大于0xFFFF的字符。 ES6 提供了String.fromCodePoint()方法,可以识别大于0xFFFF的字符,弥补了String.fromCharCode()方法的不足。

    ES5:
    String.fromCharCode(0x20BB7)
    // "ஷ" 0x20BB7就发生了溢出,最高位2被舍弃了
    
    ES6:
    String.fromCodePoint(0x20BB7)
    // "𠮷"
    String.fromCodePoint(0x78, 0x1f680, 0x79) === 'x\uD83D\uDE80y'
    // true
    

String.raw()

  • 基本用法: ES6 还为原生的String对象,提供了一个raw()方法。该方法返回一个斜杠都被转义的字符串,往往用于模板字符串的处理方法。 String.raw()方法可以作为处理模板字符串的基本方法,它会将所有变量替换,而且对斜杠进行转义,方便下一步作为字符串来使用。
    String.raw`Hi\n${2+3}!`
    // 实际返回 "Hi\\n5!",显示的是转义后的结果 "Hi\n5!"
    
    String.raw`Hi\u000A!`;
    // 实际返回 "Hi\\u000A!",显示的是转义后的结果 "Hi\u000A!"
    

实例方法

codePointAt()

  • 基本用法: JavaScript 内部,字符以 UTF-16 的格式储存,每个字符固定为2个字节。对于那些需要4个字节储存的字符(Unicode 码点大于0xFFFF的字符),JavaScript 会认为它们是两个字符。对于这种4个字节的字符,JavaScript 不能正确处理,字符串长度会误判为2,而且charAt()方法无法读取整个字符,charCodeAt()方法只能分别返回前两个字节和后两个字节的值。 ES6 提供了codePointAt()方法,能够正确处理 4 个字节储存的字符,返回一个字符的码点。
    var s = "𠮷";
    
    s.length // 2
    s.charAt(0) // ''
    s.charAt(1) // ''
    
    s.charCodeAt(0) // 55362
    s.charCodeAt(1) // 57271
    

normalize()

  • 基本用法: 许多欧洲语言有语调符号和重音符号。为了表示它们,Unicode 提供了两种方法:
    • 一种是直接提供带重音符号的字符,比如Ǒ(\u01D1)。
    • 另一种是提供合成符号(combining character),即原字符与重音符号的合成,两个字符合成一个字符,比如O(\u004F)和ˇ(\u030C)合成Ǒ(\u004F\u030C)。 这两种表示方法,在视觉和语义上都等价,但是 JavaScript 不能识别。ES6 提供字符串实例的normalize()方法,用来将字符的不同表示方法统一为同样的形式,这称为 Unicode 正规化
    ES5:
    '\u01D1'==='\u004F\u030C' //false
    
    '\u01D1'.length // 1
    '\u004F\u030C'.length // 2
    
    ES6:
    '\u01D1'.normalize() === '\u004F\u030C'.normalize()
    // true
    

includes(), startsWith(), endsWith()

  • 基本用法: 传统上,JavaScript 只有indexOf方法,可以用来确定一个字符串是否包含在另一个字符串中。ES6 又提供了三种新方法:
    • includes():返回布尔值,表示是否找到了参数字符串。
    • startsWith():返回布尔值,表示参数字符串是否在原字符串的头部。
    • endsWith():返回布尔值,表示参数字符串是否在原字符串的尾部。
    let s = 'Hello world!';
    
    s.startsWith('Hello') // true
    s.endsWith('!') // true
    s.includes('o') // true
    
    //这三个方法都支持第二个参数,表示开始搜索的位置。
    let s = 'Hello world!';
    
    //使用第二个参数n时,endsWith的行为与其他两个方法有所不同。
    //它针对前n个字符,而其他两个方法针对从第n个位置直到字符串结束。
    s.startsWith('world', 6) // true 从第n个位置直到字符串结束。
    s.endsWith('Hello', 5) // true 前n个字符
    s.includes('Hello', 6) // false 从第n个位置直到字符串结束。
    

repeat()

  • 基本用法: repeat(n)方法返回一个新字符串,表示将原字符串重复n次。
    'x'.repeat(3) // "xxx"
    'hello'.repeat(2) // "hellohello"
    'na'.repeat(0) // ""
      
    //参数如果是小数,会被取整。
    'na'.repeat(2.9) // "nana"
    
    //参数是负数或者Infinity,会报错。
    'na'.repeat(Infinity) // RangeError
    'na'.repeat(-1) // RangeError
    
    //如果参数是 0-1 之间的小数,则等同于 0,参数`NaN`等同于 0'na'.repeat(-0.9) // ""
    'na'.repeat(NaN) // ""
    
    //参数是字符串,则会先转换成数字。
    'na'.repeat('na') // ""
    'na'.repeat('3') // "nanana"
    

padStart(),padEnd()

  • 基本用法: ES2017 引入了字符串补全长度的功能。如果某个字符串不够指定长度,会在头部或尾部补全。padStart()用于头部补全,padEnd()用于尾部补全。
    //第一个参数是字符串补全生效的最大长度,第二个参数是用来补全的字符串。
    'x'.padStart(5, 'ab') // 'ababx'
    'x'.padStart(4, 'ab') // 'abax'
    
    'x'.padEnd(5, 'ab') // 'xabab'
    'x'.padEnd(4, 'ab') // 'xaba'
    
    //如果省略第二个参数,默认使用空格补全长度。
    'x'.padStart(4) // '   x'
    'x'.padEnd(4) // 'x   '
    
    //如果原字符串的长度,等于或大于最大长度,则字符串补全不生效,返回原字符串。
    'xxx'.padStart(2, 'ab') // 'xxx'
    'xxx'.padEnd(2, 'ab') // 'xxx'
    
  • 用途:
    //padStart()的常见用途是为数值补全指定位数。
    '1'.padStart(10, '0') // "0000000001"
    '12'.padStart(10, '0') // "0000000012"
    '123456'.padStart(10, '0') // "0000123456"
    
    //另一个用途是提示字符串格式。
    '12'.padStart(10, 'YYYY-MM-DD') // "YYYY-MM-12"
    '09-12'.padStart(10, 'YYYY-MM-DD') // "YYYY-09-12"
    

trimStart(),trimEnd()

  • 基本用法: ES2019 对字符串实例新增了trimStart()trimEnd()这两个方法。它们的行为与trim()一致,trimStart()消除字符串头部的空格,trimEnd()消除尾部的空格。它们返回的都是新字符串,不会修改原始字符串。
    const s = '  abc  ';
    
    s.trim() // "abc"
    s.trimStart() // "abc  "
    s.trimEnd() // "  abc"
    

    除了空格键,这两个方法对字符串头部(或尾部)的 tab 键换行符等不可见的空白符号也有效。 浏览器还部署了额外的两个方法,trimLeft()trimStart()的别名,trimRight()trimEnd()的别名。

matchAll()

  • 基本用法: matchAll()方法返回一个正则表达式在当前字符串的所有匹配。

「下次一定」

下次一定

使用微信扫描二维码完成支付