es2015

字符串的扩展

1、字符串的遍历器接口

ES6为字符串添加了遍历器接口,使得字符串可以被for…of循环遍历。

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

除了遍历字符串,这个遍历器最大的优点是可以识别大于0xFFFF的码点,传统的for循环无法识别这样的码点。
```
let text = String.fromCodePoint(0x20BB7);

for (let i = 0; i < text.length; i++) {
console.log(text[i]);
}
// “ “
// “ “
for (let i of text) {
console.log(i);
}
// “𠮷”

>上面代码中,字符串text只有一个字符,但是for循环会认为它包含两个字符(都不可打印),而for...of循环会正确识别出这一个字符。

**2、JSON.stringify()的改造**
>UTF-8标准规定,0xD800到0xDFFF之间的码点,不能单独使用,必须配对使用(**单独使用是不合法的**)。
>**JSON.stringify()的问题**在于,它可能返回0xD800到0xDFFF之间的单个码点。
>为了确保返回的是合法的UTF-8字符,**ES2019**改变了JSON.stringify()的行为。
>如果遇到0xD800到0xDFFF之间的单个码点,或者不存在的配对形式,它会返回转义字符串,留给应用自己决定下一步的处理。

JSON.stringify(‘u{D834}’) // “”uD834””
JSON.stringify(‘uDF06uD834’) // “”udf06ud834””

**3、模板字符串**
>传统的JavaScript语言,输出模板通常是这样写的。

$(‘#result’).append(
‘There are ’ + basket.count + ‘ ‘ +
‘items in your basket, ‘ +
’ + basket.onSale +
are on sale!’
);

>上面这种写法相当繁琐不方便,ES6引入了模板字符串解决这个问题。
>模块字符串是增强版的字符串,用反引号(`)标识
>可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。

$(‘#result’).append(
There are <b>${basket.count}</b> items
in your basket, <em>${basket.onSale}</em>
are on sale!
);

>使用模板字符串表示多行字符串时,所有的空格和缩进都会被保存在输出之中。
>不想要缩进的话,可以使用trim()去掉他。

$(‘#list’).html(`

  • first
  • second

`.trim());

>模板字符串中嵌入变量,需要将变量名写在 **${}** 之中
>大括号中可以放入任意的JavaScript表达式,可以进行运算,以及引用对象属性。

let x = 1;let y = 2;

${x} + ${y} = ${x + y}
// “1 + 2 = 3”

${x} + ${y * 2} = ${x + y * 2}
// “1 + 4 = 5”
let obj = {x: 1, y: 2};
${obj.x + obj.y}
// “3”

>模板字符串之中还能调用函数。

function fn() {
return “Hello World”;
}

foo ${fn()} bar
// foo Hello World bar

>如果大括号中的值不是字符串,将按照一般的规则转为字符串。
>比如,大括号中是一个对象,将默认调用对象的toString方法。
>如果模块模板字符串中的变量没有声明,将报错。
>由于模块字符串的大括号内部,就是执行JavaScript代码,因此如果大括号内部是一个字符串,将会原样输出。

Hello ${'World'}

>模板字符串甚至还能嵌套

const tmpl = addrs => `

${addrs.map(addr => `
`).join('')}

${addr.first}
${addr.last}

`;

>上面代码中,模块字符串的变量之中,又嵌入了另一个模板字符串,使用方法如下。

const data = [
{ first: ‘', last: 'Bond' },
{ first: 'Lars', last: '' },
];

console.log(tmpl(data));
// <table>
//
// <tr><td></td></tr>
// <tr><td>Bond</td></tr>
//
// <tr><td>Lars</td></tr>
// <tr><td></td></tr>
//
// </table>

>如果需要引用模板字符串本身,在需要时执行,可以写成函数。

let func = (name) => Hello ${name}!;
func(‘Jack’) // “Hello Jack!”

>上面代码中,模板字符串写成了一个函数的返回值。
>执行这个函数,就相当于执行了这个模板字符串了。

**4、标签模板**
>模板字符串的功能,不仅仅是上面这些。它可以紧跟在一个函数名后面,该函数将被调用来处理这个模板字符串。这被称为“标签模板”功能。

alert123
// 等同于
alert(123)

>但,如果模板字符里面有变量,就不是简单的调用了,而是会将模板字符串先处理成多个参数,再调用函数。

let a = 5;let b = 10;

tagHello ${ a + b } world ${ a * b };
// 等同于
tag([‘Hello ‘, ‘ world ‘, ‘’], 15, 50);

>“标签模板”的一个重要应用,就是过滤HTML字符串,防止用户输入恶意内容。

let message =
SaferHTML<p>${sender} has sent you a message.</p>;

function SaferHTML(templateData) {
let s = templateData[0];
for (let i = 1; i < arguments.length; i++) {
let arg = String(arguments[i]);

// Escape special characters in the substitution.
s += arg.replace(/&/g, "&amp;")
        .replace(/</g, "&lt;")
        .replace(/>/g, "&gt;");

// Don't escape special characters in the template.
s += templateData[i];   }   return s; } ``` >上面代码中,sender变量往往是用户提供的,经过SaferHTML函数处理,里面的特殊字符都会被转义。 ``` let sender = '<script>alert("abc")</script>'; // 恶意代码 let message = SaferHTML`<p>${sender} has sent you a message.</p>`;

message
// <p><script>alert(“abc”)</script> has sent you a message.</p>

>标签模板的另一个应用,就是多语言转换(国际化处理)。

**5、模板字符串的限制**
>前面提到标签模板里面,可以内嵌其他语言。但是,模板字符串默认会将字符串转义,导致无法嵌入其他语言。
>举例来说,标签模板里面可以嵌入 LaTEX 语言。

function latex(strings) {
// …
}

let document = latex`
newcommand{fun}{textbf{Fun!}} // 正常工作
newcommand{unicode}{textbf{Unicode!}} // 报错
newcommand{xerxes}{textbf{King!}} // 报错

Breve over the h goes u{h}ere // 报错
`

>上面代码中,变量document内嵌的模板字符串,对于 LaTEX 语言来说完全是合法的,但是 JavaScript 引擎会报错。
>原因就在于字符串的转义。模板字符串会将u00FF和u{42}当作 Unicode 字符进行转义,所以unicode解析时报错;
>而x56会被当作十六进制字符串转义,所以xerxes会报错。也就是说,u和x在 LaTEX 里面有特殊含义,但是 JavaScript 将它们转义了。
>为了解决这个问题,ES2018 放松了对标签模板里面的字符串转义的限制。
>如果遇到不合法的字符串转义,就返回undefined,而不是报错,并且从raw属性上面可以得到原始字符串。

function tag(strs) {
strs[0] === undefined
strs.raw[0] === “unicode and u{55}”;
}
tagunicode and u{55}

>上面代码中,模板字符串原本是应该报错的,
>但是由于放松了对字符串转义的限制,所以不报错了,JavaScript 引擎将第一个字符设置为undefined,
>但是raw属性依然可以得到原始字符串,因此tag函数还是可以对原字符串进行处理。
>注意,这种对字符串转义的放松,**只在标签模板解析字符串时生效,不是标签模板的场合,依然会报错**。

let bad = bad escape sequence: unicode; // 报错
```