• 字符串的扩展


    字符串解读

    es6加强了对Unicode 的支持,允许\uxxxx的形式展现一个字符,例如:

    console.log('\u0061'); // 打印 a
    
    • 1

    \u后面的为字符的 Unicode 码点 \u 后面4位 xxxx

    但是这种写法只识别 \u0000\UFFFF 之间的字符,超出需要使用两个双字节表示,例如:

    console.log('\uD842\uDFB7'); // 打印 吉
    
    • 1

    如果说超出了\uxxxx字节的范围,则为两个字节的拼接,例如:

    console.log('\u20BB7'); // 输出 ' 7'   \u20BB系统识别为空
    console.log('\u00617'); // 输出 'a7'   
    
    • 1
    • 2

    \u0061识别为a,由于7超出了这个字节,所以为\u0061+7,结果为a7

    es6Unicode 的支持进行了加强,如果超出了两个字节,放入大括号内即可正常解读

    console.log("\u{20BB7}"); // 打印 吉
    
    // 只要将码点放入大括号即可正确解读
    console.log('\u{41}\u{42}\u{43}'); // 输出ABC
    
    • 1
    • 2
    • 3
    • 4

    大括号与双字节的写法是等价的

      console.log('\u{1F680}' == '\uD83D\uDE80'); // 大括号与4字节的写法等价 输出true
    
    • 1

    js对字符的几种表现方法:

    console.log('\z' === 'z');
    console.log('\172' === 'z');
    console.log('\x7A' === 'z');
    console.log('\u007A' === 'z');
    console.log('\u{7A}' === 'z');
    console.log('z' === 'z');
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    字符串的遍历

    字符串遍历for...of

      for (let codePoint of 'foo') {
         console.log(codePoint); // f o o
       }
    
    • 1
    • 2
    • 3

    其实一般的遍历,例如for,也可以遍历字符串,但是for无法识别大于0xFFFF的码点,而for...of则可以识别

     let text = String.fromCodePoint(0x20BB7)
     
     // for循环
        for (let i = 0; i < text.length; i++) {
             console.log(text[i]); // ' ' 空
          }
          
    // for---of可以识别 大于0xFFFF的码点 , 而传统的for无法识别
        for (let i of text) {
            console.log(i); // 吉
         }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    有些时候,我们在用JSON.stringify转字符串的时候,发现转译的字符串多了几个\
    根据标准,JSON数据必须是 UTF-8 编码。但是JSON.stringify()方法有可能返回不符合 UTF-8 标准的字符串。
    UTF-8 标准规定,0xD8000xDFFF之间的码点,不能单独使用,必须配对使用。比如,\uD834\uDF06是两个码点,但是必须放在一起配对使用,代表字符𝌆。这是为了表示码点大于0xFFFF的字符的一种变通方法。单独使用\uD834\uDF06这两个码点是不合法的,或者颠倒顺序也不行,因为\uDF06\uD834并没有对应的字符。
    JSON.stringify()的问题在于,它可能返回0xD8000xDFFF之间的单个码点。

    JSON.stringify('\u{D834}') // "\u{D834}"
    
    • 1

    所以 es2019JSON.stringify()做出了改变,如果遇到0xD8000xDFFF之间的单个码点,或者不存在的配对形式,它会返回转义字符串,留给应用自己决定下一步的处理。

    console.log(JSON.stringify('\u{D834}')) // ""\\uD834""
    console.log(JSON.stringify('\uDF06\uD834')) // ""\\uD834""
    
    • 1
    • 2

    模板字符串

    1、模板字符串识别标签,并却可以识别多行内容,传统的写法需要用+ 号连接,
    2、模板字符串识别空格

    let context = document.getElementById('context')
    let str = `
    东方 不败
    东方求败
    `
    console.log(context.innerHTML = str); // 页面显示 东方 不败
    • 1
    • 2
    • 3
    • 4

    在这里插入图片描述
    传统的字符串插值,需要使用"字符"+100+"值"的形式,用 + 拼接,值是不能嵌套在引号当中的,否则会解译为字符串。

    console.log("
    东方不败有" + 100 + "元
    "
    )
    • 1

    模板字符串直接使用${xxx}即可在字符串中插值,并且在里面可以使用表达式以及调用函数。

    let str2 = `
    东方不败${100}
    `
    // 东方不败 100 // 表达式 let s = 100 let str3 = `
    东方不败${s == 100 ? '有100元' : '没有100元'}
    `
    // 东方不败有100元 // 调用函数 let str4 = `
    调用函数:${text2()}
    `
    function text2() { return '东方不败' } let context2 = document.getElementById('context2') console.log(context2.innerHTML = str4); // 页面显示 调用函数:东方不败
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    模板字符串可以嵌套使用,此处用的是map遍历结构,需要注意的是,forEach是无法在此遍历结构的,会直接报错,因为forEach会改变原数组,而map则不会(数组为基础类型时原数组不变)。

    let context3 = document.getElementById('context3')
    let arr = [{
                name: '字符串',
                index: '01'
            }, {
                name: '字符串',
                index: '02'
            }]
    let s2 = `
            
    模板字符串嵌套:${ arr.map(el => `
    ${el.name}
    ${el.index}
    `
    ) }
    `
    context3.innerHTML = s2
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    结果:
    在这里插入图片描述


    标签模板

    函数名跟上模板字符串,则为标签模板,左边是函数,右边实际上是函数参数,例如:

    alert `hello` // 等同于 alert(['hello'])
    
    • 1

    此处的alert是函数,紧跟在后面的模板字符串就是它的参数,这里会触发alert弹框,展示hello
    但如果模板字符串有变量,就不是简单的调用,而是会先将模板字符串先处理成多个参数,再调用函数,例如:

    let a = 5
    let b = 10
    // alert `hello ${ a + b} , word ${ a * b }`
    tag(`hello ${ a + b} , word ${ a * b }`)
    
    • 1
    • 2
    • 3
    • 4

    此处的tag等同于 tag([hello ', ', word ', ''],15,50),在这里,模板字符串前有一个tag,这个tag是一个函数,整个表达式的返回值就是tag函数处理模板字符串后返回的值,返回结果可以看上面alert打印的内容。
    实际上是将tag转换成了:

     // 实际上转换成了
    function tag(stringArr, value1, value2) {
           // ......
     }
    // 或者
    function tag(stringArr, ...values) {
          console.log(stringArr, values);
          // ......
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    1、tag函数的第一个参数是一个数组,整个数组是模板字符串中没有变量替换的部分。
    2、变量的替换,只发生在数组的第一个成员于第二个成员之间,第二个成员与第三个成员之间,以此类推。
    3、tag函数的其他参数,都是模板字符串各个变量被替换后的值。这里的模板字符串有两个参数,所以这里会接收 value1 value2 两个参数。
    例如:
    第一个参数:[hello ', ', word ', '']
    第二个参数:15
    第三个参数:50
    其实也就是 tag([hello ', ', word ', ''],15,50)

    这里再举一个例子:
    下面就是关于标签模板是怎样将字符串与值拼接的过程,最终展现的就是标签模板编译后的结果

    let total = 30;   // 变量
    let msg = passthru `The total is ${total} (${total*1.05} with tax)`;
    
    function passthru(literals) {
    //literals : ['The total is ', ' (', ' with tax)', raw: Array(3)]
         let result = ''
         let i = 0
         while (i < literals.length) {
              result += literals[i++];
              if (i < arguments.length) {
              /* arguments:
              0:['The total is ', ' (', ' with tax)', raw: Array(3)]
              1 : 30
              2 : 31.5
              */
         console.log(arguments); // 参数的数组  
         result += arguments[i]
               }
            }
          return result
       }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    输出结果:The total is 30 (31.5 with tax)
    步骤拆解:
    1: passthru函数的参数literals就是标签模板的参数['The total is ', ' (', ' with tax)', raw: Array(3)]
    2: while 遍历了数组参数的长度,并且在内部进行判断
    3: if中的arguments就是参数的数组,这一步就是关键的字符串与值得拼接,拼接的步骤如下:

    while遍历,如果参数为true则循环遍历,直到false终止
    遍历内容如下:
       The total is 
       30
       (
       31.5
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    前面说过了:变量的替换,只发生在数组的第一个成员于第二个成员之间,第二个成员与第三个成员之间,以此类推。所以此处也是这样处理的,最后返回的结果就是The total is 30 (31.5 with tax)

    恶意输入

    标签模板还有一个重要的作用就是防止用户恶意输入,如果用户在输入框恶意嵌套标签是非常不安全的行为。

    let sender = '