4

字符串的解读和标签模板

 1 year ago
source link: https://www.fly63.com/article/detial/12377
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

字符串解读

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

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

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

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

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

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

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

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

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

console.log("\u{20BB7}"); // 打印 吉

// 只要将码点放入大括号即可正确解读
console.log('\u{41}\u{42}\u{43}'); // 输出ABC

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

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

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');

字符串的遍历

字符串遍历for...of

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

其实一般的遍历,例如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); // 吉
     }

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

JSON.stringify()的问题在于,它可能返回0xD800到0xDFFF之间的单个码点。

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

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

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

模板字符串

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

let context = document.getElementById('context')
let str = `<div>东方 不败</div>
           <div>东方求败</div>`
console.log(context.innerhtml = str); // 页面显示 东方 不败

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

console.log("<div>东方不败有" + 100 + "元</div>")

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

let str2 = `<div>东方不败${100}</div>` // 东方不败 100

// 表达式
let s = 100
let str3 = `<div>东方不败${s == 100 ? '有100元' : '没有100元'}</div>` // 东方不败有100元

// 调用函数
let str4 = `<div>调用函数:${text2()}</div>`
function text2() {
            return '东方不败'
        }
let context2 = document.getElementById('context2')
console.log(context2.innerHTML = str4); // 页面显示 调用函数:东方不败  

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

let context3 = document.getElementById('context3')
let arr = [{
            name: '字符串',
            index: '01'
        }, {
            name: '字符串',
            index: '02'
        }]
let s2 = `
        <div>模板字符串嵌套:${
        arr.map(el => `
                <div>${el.name}</div>
                <div>${el.index}</div>
            `)
       }</div>
       `
        context3.innerHTML = s2

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

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

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

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

此处的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、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
   }

输出结果: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

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

恶意输入

标签模板还有一个重要的作用就是防止用户恶意输入,如果用户在输入框恶意嵌套标签是非常不安全的行为。
let sender = '<script>alert("恶意代码")</cript>'

function SaferHTML(templateData) {
        let s = templateData[0]
        for (let i = 1; i < arguments.length; i++) {
             let arg = String(arguments[i])
             s +=arg.replace(/&/g,"&amp")
                    .replace(/</g,"&lt")
                    .replace( />/g,"&gt") 
     // 过滤 转义 &为&amp字符;  <为&lt字符;  >为&gt字符;
             s += templateData[i]
            }
            return s
        }

此处将用户嵌套的script标签进行了转译,&为&amp字符、<为&lt字符、 >为&gt字符

let message = SaferHTML `<p>${sender}个人信息</p>`
console.log(message);
// 打印 <p><script>alert("恶意代码")</script> 个人信息</p>
由于我的编译器会自动格式化,所以用 sxxxcript 代替 script

标签模板可以做多语言转(国际化)

i18n `Welcome to ${userName}, you are visitor number ${visitorNumber}!`

甚至可以在标签模板嵌套其他语言

jsx `
   <div>
      <input
       ref='input'
       onChange='${this.handleChange}'
       defaultValue='${this.state.value}' />
       ${this.state.value}
       </div>
 `
// 此处就是通过jsx函数,将dom字符串转换为react对象

模板处理函数的第一个参数,也就是非参数的模板字符串数组,有一个raw属性

 console.log(`abc`) // ['abc',row:Array[1]]
// 这个raw保存的是转义后的原字符串

这个数组后面的raw保存的是转义后的原字符串。


案例源码:https://gitee.com/wang_fan_w/es6-science-institute

如果觉得这篇文章对你有帮助,欢迎点亮一下star

原文来自:https://www.cnblogs.com/wang-fan-w/archive/2023/02/14/17119878.html

链接: https://www.fly63.com/article/detial/12377


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK