一.作用及语法
模板字符串用来实现字符串插值,比加号(+)拼接更方便更优雅
`${expr}`
语法,提供了字符串插值功能
注意:上面的`
(反撇号,位于键盘Esc
下面)是模板字符串语法的一部分,而不是markdown用错了
P.S.笔者遇到了第一个问题,反撇号与markdown语法有冲突,有两种选择,要么使用markdown特性之多行反撇好作为代码分隔符(用两个连续反撇号包裹代码,但笔者使用的markdown插件不支持该罕见语法),要么使用code标签,比如上面的markdown源码是<code>\`${expr}\`</code>
例如:
function req(user, action) {
// if !isValid
error(`user ${user} failed to do action ${action}`);
}
function error(str) {
console.log(str);
}
// test
req('Sam', 'login'); // user Sam failed to do action login
二.特点
1.expr可以是任意合法js表达式,且支持嵌套
包括变量、字面量、函数调用等等,但不支持流程控制(分支/循环),所以模板字符串不能代替模板引擎
例如:
// 不支持分支/循环
// console.log(`${if (2 > 1) {str}}`); // Uncaught SyntaxError: Unexpected token if
// console.log(`${for (; false;) {}}`); // Uncaught SyntaxError: Unexpected token for
var outer = 'outer';
var inner = 'inner';
var str = `
\$\{
${
outer // 变量
+ // +拼接
`\$\{
${inner} // 插值
\}`
}
\}
`;
var h5 = document.querySelector('#h5');
var pre = document.querySelector('#pre');
h5.innerHTML = str;
pre.innerHTML = str;
显示结果如下:
// #h5
${ outer${ inner // 插值 } }
// #pre
${
outer${
inner // 插值
}
}
3.可以分多行书写
但注释(单行/多行)可能会作用于整个串,或者直接作为字面量出现在结果串里。模板字符串中所有的空格、新行、缩进,都会原样输出在生成的字符串中(pre与其它元素不同),具体见上例
4.特殊字符需要转义
3个特殊字符[`${]
(正则表达式形式)需要转义,具体见上例
P.S.右花括号不需要转义,当然,转义它也没错。但是上面3个必须转义
5.没有自动处理
不会自动过滤不安全标签,也不会自动转义特殊字符,同样需要防止xss攻击,虽然提供了标签模板(tagged templates),但不很方便
例如:
// xss攻击
var userInput = '<a href="" onclick="alert(\'xss attack\');">领取奖励</a>';
var input = document.querySelector('#input');
input.innerHTML = `用户输入:${userInput}`;
上面代码会生成一个链接,点击alert “xss attack”,通过标签模板可以对模板字符串进行自定义处理,比如滤掉不安全字符,如下:
// 定义模板标签
function myFilter(templateData) {
var aStr = templateData; // 被${}插值分割后的字符串数组
var aRaw = templateData.raw; // 未经转义的原字符串(\n)
var aVar = Array.prototype.slice.call(arguments, 1); // 插值变量数组
console.log(aStr);
console.log(aRaw);
console.log(aVar);
// filter vars
aVar.forEach(function(item, index, arr) {
arr[index] = item.replace(/(<[^>]*>)/i, '');
});
var res = '';
var i = 0;
aStr.forEach(function(item) {
res += item;
if (aVar[i]) {
res += aVar[i++];
}
});
return res;
}
// 使用标签模板
var safeInput = document.querySelector('#safe_input');
safeInput.innerHTML = myFilter`用户输入:\n${userInput}`;
// 等价于
// safeInput.innerHTML = myFilter(['用户输入:'], userInput);
标签出现在模板字符串前,是一种简化的函数调用,模板标签类似于回调函数,参数templateData
的格式是确定的,从参数中取出数据,处理完毕返回即可
但定义模板标签感觉还是比较麻烦,但这是一个很有吸引力的特性,预示着一个强大的字符串处理库(转义特殊字符、i18n、字数统计等等)
6.灵活性
标签模板的返回值不一定是字符串,可以是任何值。这意味着不仅仅会出现一个强大的“字符串”处理库,任何库都可以以这种形式完美嵌入JS内,尤其是着色器语言这样的目前在JS中形式不太友好的东西……其它语言出现诱人的特性后,可以完美植入JS,甚至可以借此实现一种自己的语言(模板标签的函数体就是解释器)
标签模板带来很大的灵活性,可以用自定义的标签来创建正则表达式、DOM树、图片、以promises为代表的整个异步过程、JS数据结构、GL着色器……
引用参考资料中的原文:
标签模板以开放的姿态欢迎库设计者们来创建强有力领域特定语言。这些语言可能看起来不像JS,但是它们仍可以无缝嵌入到JS中并与JS的其它语言特性智能交互。我不知道这一特性将会带领们走向何方,但它蕴藏着无限的可能性,这令我感到异常兴奋!
三.总结
模板字符串看似语法糖,其实是打开了一扇大门,表示JS以开放的姿态迎接未来
还记得前辈们5年前讨论的yield吗?那时候他们多希望有个这样的特性啊,讨论这个问题的初衷不就是希望JS能获得C#的yield特性吗?
参考资料
- 《ES6 in Depth》:InfoQ中文站提供的免费电子书