context.filltext(`ceci n'est pas une cha?ne.`, x, y); context.filltext(`ceci n'est pas une cha?ne.`, x, y);
之所以被称为模板字符串,是因为模板字符串为 js 引入了简单的字符串插值特性,也就是说,可以方便优雅地将 js 的值插入到字符串中。
很多地方可以用到模板字符串,看下面这个不起眼的错误提示消息:
function authorize(user, action) { if (!user.hasprivilege(action)) { throw new error( `user ${user.name} is not authorized to do ${action}.`); }} function authorize(user, action) { if (!user.hasprivilege(action)) { throw new error( `user ${user.name} is not authorized to do ${action}.`); }}
上面代码中,${user.name} 和 ${action} 被称为模板占位符,javascript 将把 user.name和 action 的值分别插到对应的位置上,然后生成像这样 “user jorendorff is not authorized to do hockey.” 的字符串。
现在,我们看到了一个比 + 运算符更优雅的语法,下面是一些你期待的特性:
模板占位符可以是任何 javascript 表达式,所以函数调用和四则运算等都是合法的。(甚至你还可以在一个模板字符串中嵌套另一个模板字符串。)
如果一个值不是字符串,它将被转换为字符串。例如,如果 action 是一个对象,那么该对象的 .tostring() 将被调用,来将其转换为字符串。
如果你想在模板字符串中使用反引号,你需要使用反斜杠 \ 将其转义。
同样地,如果想在模板字符串中输出 ${,也需要使用反斜杠将其转义:${ 或 $\{。
模板字符串可以跨越多行:
$(#warning).html(` watch out! unauthorized hockeying can result in penalties of up to ${maxpenalty} minutes.
`); $(#warning).html(` watch out! unauthorized hockeying can result in penalties of up to ${maxpenalty} minutes.
`);
模板字符串中所有的空格、换行和缩进,都将被原样输出到结果字符串中。
下面我们来看看模板字符串做不到的事情:
不会自动转义特殊字符,为了避免跨站脚本漏洞,你还是需要小心对待不可信的数据,这一点上与普通字符串一样。
不能与国际化库配合使用,不处理特殊语言格式的数字、日期等。
不是模板引擎(比如 mustache 或 nunjucks)的替代品。模板字符串没有处理循环的语法 — 不能通过一个数组构建出一个表格(table)。
为了解决这些限制,es6 为开发者和库设计者提供了另一种模板字符串 — 标签模板。
标签模板的语法很简单,只需要在开始的反引号前引入一个标签。看第一个例子:saferhtml,我们要使用这个标签模板来解决上述的第一个限制:自动转义特殊字符。
需要注意的是,saferhtml 方法并不是 es6 标准库提供的,我们需要自己来实现:
var message = saferhtml`${bonk.sender} has sent you a bonk.
`; var message = saferhtml`${bonk.sender} has sent you a bonk.
`;
这里的 saferhtml 标签是单个标识符,标签也可以是属性,比如 saferhtml.escape,甚至还可以是方法调用:saferhtml.escape({unicodecontrolcharacters: false})。准确地说,任何 es6 的成员表达式或调用表达式都可以作为标签。
可以看出,模板字符串仅仅是字符串连接的语法糖,而标签模板确是一个完全不同的东西:函数调用。
所以,上面代码等价于:
var message = saferhtml(templatedata, bonk.sender); var message = saferhtml(templatedata, bonk.sender);
其中 templatedata 是一个不可变的字符串数组,由 js 引擎基于源模板字符串生成,这里的数组含有两个元素,因为模板字符串被占位符分隔后含有两个字符串,因此,templatedata 将是这样: object.freeze([, has sent you a bonk.
]
(事实上,templatedata 上还有另一个属性:templatedata.raw,本文并深入不讨论该属性。该属性的值也是一个数组,包含了标签模板中所有的字符串部分,但字符串中包含了转义序列,看上去更像源代码中的字符串,比如 \n。es6 的内置标签 string.raw 将使用这些字符串。)
这就使得 saferhtml 方法可以随意解析这两个字符串,存在 n 中替换方式。
在继续阅读钱,你可能在苦苦思索如何实现 saferhtml 方法。
下面是一种实现(gist):
function saferhtml(templatedata) { var s = templatedata[0]; for (var i = 1; i < arguments.length; i++) { var arg = string(arguments[i]); // escape special characters in the substitution. s += arg.replace(/&/g, &) .replace(/); // don't escape special characters in the template. s += templatedata[i]; } return s;} function saferhtml(templatedata) { var s = templatedata[0]; for (var i = 1; i hallo bob, sie haben 1.234,56 $ca auf ihrem bankkonto.i18n`hello ${name}, you have ${amount}:c(cad) in your bank account.`// => hallo bob, sie haben 1.234,56 $ca auf ihrem bankkonto.
上面例子中的 name 和 amount 很好理解,将被 js 引擎替换为对应的字符串,但是还有一个没有见过的占位符::c(cad),这将被 i18n 标签处理,从 i18n 的文档可知::c(cad)表示 amount 是加拿大美元货币值。
模板字符串不能替代 mustache 和 nunjucks 这类模板引擎,部分原因在于模板字符串不支持循环和条件语句。我们可以编写一个标签来实现这类功能:
// purely hypothetical template language based on// es6 tagged templates.var libraryhtml = hashtemplate` #for book in ${mybooks} #{book.title} by #{book.author} #end `; // purely hypothetical template language based on// es6 tagged templates.var libraryhtml = hashtemplate` #for book in ${mybooks} #{book.title} by #{book.author} #end `;
灵活性还不止于此,需要注意的是,标签函数的参数不会自动转换为字符串,参数可以是任何类型,返回值也一样。标签模板甚至可以不需要字符串,你可以使用自定义标签来创建正则表达式、dom 树、图片、代表整个异步进程的 promise、js 数据结构、gl 着色器…
标签模板允许库设计者创建强大的领域特定语言。这些语言可能看上去并不像 js,但他们可以无缝嵌入到 js 中,并且可以与语言的其余部分进行交互。顺便说一下,我还没有在其他语言中见过类似的特性,我不知道这个特性讲给我们带来些什么,但各种可能性还是非常令人兴奋的。
