字符串拓展
字符的 Unicode 表示法
- ES6 加强了对 Unicode 的支持,允许采用
\uxxxx
形式表示一个字符,其中xxxx表示字符的 Unicode 码点。 - 只限于码点在
\u0000~\uFFFF
之间的字符。超出这个范围的字符,必须用两个双字节的形式表示
,或者将码点放入大括号,就能正确解读该字符。
// 使用 Unicode 表示 "𠮷"
"\u20BB7" // " 7" 直接使用 ×
"\uD842\uDFB7" // "𠮷" 使用双字节 √
"\u{20BB7}" // "𠮷" 使用 {} √
// 使用 Unicode 表示 "𠮷"
"\u20BB7" // " 7" 直接使用 ×
"\uD842\uDFB7" // "𠮷" 使用双字节 √
"\u{20BB7}" // "𠮷" 使用 {} √
上面示例 "𠮷" 的表示方式,由于
20BB7
大于FFFF
,javascript会理解成\u20BB+7
,而\u20BB
是一个不可打印字符,所以只会显示一个空格,后面跟着一个 7。
字符及字符转义
- JavaScript字符串允许直接输入字符,或者字符的转义形式,两者是等价的。如:
'中' === '\u4e2d'
- JavaScript 规定有5个字符,不能在字符串里面直接使用,只能使用转义形式。
- U+005C:反斜杠(reverse solidus)
- U+000D:回车(carriage return)
- U+2028:行分隔符(line separator)
- U+2029:段分隔符(paragraph separator)
- U+000A:换行符(line feed)
字符串里面不能直接包含反斜杠(被转义,得不到想要的结果,或者报错),一定要转义写成\\
或者\u005c
。
注意
JSON 格式允许字符串里面直接使用 U+2028(行分隔符)和 U+2029(段分隔符),这样一来,服务器输出的 JSON 被JSON.parse解析,就有可能直接报错。
在ES2019 允许 JavaScript 字符串直接输入 U+2028(行分隔符)和 U+2029(段分隔符)。
但正则表达式依然不允许直接输入 U+2028(行分隔符)和 U+2029(段分隔符)这两个字符。
JSON.stringify()
存在问题
根据标准,JSON 数据必须是 UTF-8 编码。但是,现在的JSON.stringify()方法有可能返回不符合 UTF-8 标准的字符串。
UTF-8 标准规定,0xD800到0xDFFF之间的码点,不能单独使用,必须配对使用。 比如,\uD834\uDF06是两个码点,但是必须放在一起配对使用,代表字符𝌆。这是为了表示码点大于0xFFFF的字符的一种变通方法。单独使用\uD834和\uDFO6这两个码点是不合法的,或者颠倒顺序也不行因为\uDF06\uD834并没有对应的字符。
解决方案
ES2019 改变了JSON.stringify()的行为,确保返回是合法的 UTF-8 字符。
如果遇到0xD800到0xDFFF之间的单个码点,或者不存在的配对形式,它会返回转义字符串,留给应用自己决定下一步的处理。
JSON.stringify('\u{D834}') // ""\\uD834""
JSON.stringify('\uDF06\uD834') // ""\\udf06\\ud834""
JSON.stringify('\u{D834}') // ""\\uD834""
JSON.stringify('\uDF06\uD834') // ""\\udf06\\ud834""
模板字符串
模板字符串(template string)是增强版的字符串,用反引号 (`)标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。
`string text`
`string text line 1
string text line 2`
`string text ${expression} string text`
tagFunction`string text ${expression} string text`
`string text`
`string text line 1
string text line 2`
`string text ${expression} string text`
tagFunction`string text ${expression} string text`
- 通过使用占位符 ${expression} 嵌入待替换的表达式,大括号内部可以放入任意的 JavaScript 表达式,可以进行运算,以及引用对象属性。表达式执行完成厚,强制转换为字符串。
- 若要转义模板字面量中的反引号(`),需在反引号之前加一个反斜杠(\)。
- 字符串字面量中的转义序列都是允许的,任何其他格式不正确的转义序列都是语法错误。
\ 后跟 0 以外的任何十进制数字,语法报错;
\x 后跟两位以下十六进制数字,语法报错;
\u 后不跟 {},并且后跟四个以下十六进制数字,语法报错;
模板编译
通过模板字符串,生成正式模板的实例。
let template = `
<ul>
<% for(let i=0; i < data.supplies.length; i++) { %>
<li><%= data.supplies[i] %></li>
<% } %>
</ul>
`;
let template = `
<ul>
<% for(let i=0; i < data.supplies.length; i++) { %>
<li><%= data.supplies[i] %></li>
<% } %>
</ul>
`;
该模板使用<%...%>
放置 JavaScript 代码,使用<%= ... %>
输出 JavaScript 表达式。
// compile 封装
function compile(template){
const evalExpr = /<%=(.+?)%>/g;
const expr = /<%([\s\S]+?)%>/g;
template = template
.replace(evalExpr, '`); \n echo( $1 ); \n echo(`')
.replace(expr, '`); \n $1 \n echo(`');
template = 'echo(`' + template + '`);';
let script =
`(function parse(data){
let output = "";
function echo(html){
output += html;
}
${ template }
return output;
})`;
return script;
}
// compile 封装
function compile(template){
const evalExpr = /<%=(.+?)%>/g;
const expr = /<%([\s\S]+?)%>/g;
template = template
.replace(evalExpr, '`); \n echo( $1 ); \n echo(`')
.replace(expr, '`); \n $1 \n echo(`');
template = 'echo(`' + template + '`);';
let script =
`(function parse(data){
let output = "";
function echo(html){
output += html;
}
${ template }
return output;
})`;
return script;
}
调用 compile 函数
let parse = eval(compile(template));
div.innerHTML = parse({ supplies: [ "broom", "mop", "cleaner" ] });
// <ul>
// <li>broom</li>
// <li>mop</li>
// <li>cleaner</li>
// </ul>
// eval 函数是一个非常危险的函数
let parse = eval(compile(template));
div.innerHTML = parse({ supplies: [ "broom", "mop", "cleaner" ] });
// <ul>
// <li>broom</li>
// <li>mop</li>
// <li>cleaner</li>
// </ul>
// eval 函数是一个非常危险的函数
标签模板
模板字符串,它可以紧跟在一个函数名后面。该函数将被调用来处理这个模板字符串。这被称为 标签模板
功能(tagged template)。
alert`hello`
// 等同于
alert(['hello'])
alert`hello`
// 等同于
alert(['hello'])
标签模板其实不是模板,而是函数调用的一种特殊形式。“标签”指的就是函数,紧跟在后面的模板字符串就是它的参数。
- 函数的第一个参数是一个数组,该数组的成员是模板字符串中那些没有变量替换的部分;
- 整个表达式的返回值,就是函数处理模板字符串后的返回值。
函数参数:
第一个参数:[ ] ,是一个数组,是模板字符串中那些没有变量替换的部分
后面的参数: ...rest,是${...} 表达式的值
let a = 5;
let b = 10;
function tag(s, v1, v2) {
console.log(s[0]);
console.log(s[1]);
console.log(s[2]);
console.log(v1);
console.log(v2);
return "OK";
}
tag`Hello ${ a + b } world ${ a * b}`;
// "Hello "
// " world "
// ""
// 15
// 50
// "OK" 这是函数返回值
let a = 5;
let b = 10;
function tag(s, v1, v2) {
console.log(s[0]);
console.log(s[1]);
console.log(s[2]);
console.log(v1);
console.log(v2);
return "OK";
}
tag`Hello ${ a + b } world ${ a * b}`;
// "Hello "
// " world "
// ""
// 15
// 50
// "OK" 这是函数返回值
标签模板的应用:
- 就是过滤 HTML 字符串,防止用户输入恶意内容。
- 多语言转换(国际化处理)。
- jsx函数封装等等。
字符新增方法
静态方法:
- String.fromCodePoint()
- String.raw()
实例方法:
- String.prototype.codePointAt()
- String.prototype.normalize()
- String.prototype.includes()
- String.prototype.startsWith()
- String.prototype.endsWith()
- String.prototype.replace()
- String.prototype.padStart()
- String.prototype.padEnd()
- String.prototype.trimStart()
- String.prototype.trimEnd()
- String.prototype.matchAll()