【探秘ES6】系列专栏(四):模版字符串

ES6做为新一代JavaScript标准,即将与广大前端开发者见面。为了让你们对ES6的诸多新特性有更深刻的了解,Mozilla Web开发者博客推出了《ES6 In Depth》系列文章。CSDN已获受权,将持续对该系列进行翻译,组织成【探秘ES6】系列专栏供你们学习借鉴。本文为该系列的第四篇。html

前两次学习了生成器迭代器之后,脑壳有没有一团浆糊?哈哈。我承诺过本次咱们将学习一些简单的东西。前端

那咱们如今就开始吧!git

“小句号”的基本使用程序员

ES6新引入了一种新的字符串语法——模版字符串(Template Strings),它看起来和普通的字符串很像,区别在于它不是由单引号'或者双引号"来闭合,而是使用`(俗称:小句号)。咱们来看个最简单的例子,它们其实就是字符串而已:es6

context.fillText(`Ceci n'est pas une chaîne.`, x, y);

可是既然它被叫作“模版字符串”,那它应该不只仅是用小句号来闭合的普通字符串吧?模版字符串让JavaScript有了一个简单的 字符串插值 功能——既写法美观又能很方便地将JavaScript变量替换到字符串中。

不少情景下均可以用到它,但最打动个人是它在绝不起眼的错误消息中的使用:github

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(你能够本身试试,我可没忽悠你)。

目前为止,它只是个比+(加号链接符)在语法上略微美观一些而已,你可能想了解更多关于它的细节:web

  • 模版替换位中的代码能够是任何JavaScript的表达式,好比函数调用、算数运算等等都是容许的(只要你乐意,你甚至能够在模版字符串中嵌套模版字符串,有种模版中的《盗梦空间》的感受)。正则表达式

  • 若是插入的值不是字符串,它将被转换为字符串。例如,若是action是个对象,它的.toString()方法将会被调用。typescript

  • 若是你的模版字符串中包含 ` (小句号),须要使用转义字符`\``,效果至关于 "`"。express

  • 触类旁通,若是你的模版字符串中须要 ${ 这两个字符,我不想去关心你为何要这么写,可是你可使用转义其中任意一个字符:`write \${  或者 $\{`。

与普通字符串不一样的是,模版字符串能够写成多行:

$("#warning").html(`  
  <h1>Watch out!</h1>  
  <p>Unauthorized hockeying can result in penalties  
  of up to ${maxPenalty} minutes.</p>  
`);

在模版字符串中,全部空格、换行、缩进都是逐字输出的。

好的,就像我上篇文章所承诺的,我以为我必须对你大脑的健康负责。因此给一个小小的警告:接下来的内容就比较费脑一点了。你能够如今就放弃阅读,喝杯咖啡享受一下,放松一下大脑。认真地说,不用为放弃下面的内容而感到羞愧噢。 当Lopes Gonçalves穿过了赤道,证实了不会被可怕的海怪吃掉或掉进地球的尽头,而后他还须要去航行整个完整的南半球来增长论证吗?不须要!他返航了,回到家吃了顿美美的午饭。换作是你也会这么作,对不对?

深刻讨论“小句号”

咱们来讨论一些模版字符串的局限性。

  • 它不会为你自动转义关键字。为了防止脚本注入,你须要认真当心对待那些不受信任的数据,就像你曾经当心地拼接字符串同样。

  • 它并无明确地说明如何配合国际化库(一个针对用户所处的国家/语言不一样来国际化你的代码的库)来使用。模版字符串不会处理特定语言的数字、日期等的格式化。

  • 它并不能代替像MustacheNunjucks这样的模板库。

模版字符串没有内置针对循环的语法——经过数组来循环生成一个HTML表格;甚至条件语句也不支持。(固然,你可使用模版嵌套来实现,可是这样的方法太笨拙,我认为不可取。)

ES6在模版字符串中为JS开发者和库设计人员提供了一种解决方案来帮助他们处理这些局限性问题。这个特性被称作模版标记。

模版标记的语法很简单。咱们只须要在“小句号”的前面加一个额外的标记。在咱们接下来的第一个例子中,SaferHTML就是它的标记,咱们使用这个标记来处理上面列表中的第一个局限性问题:自动转义关键字。

必须清楚SaferHTML并非ES6标准库中的内容。在下面咱们将会本身来实现它。

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

这里使用了一个标识符SaferHTML来做为标记,标记也能够是一个属性——SaferHTML.escape,甚至能够是一个函数调用——SaferHTML.escape({unicodeControlCharacters: false})。(说明一下,任何ES6的 MemberExpressio类或CallExpression类 均可以做为标记使用)。

能够看出不含标记的模版字符串适用于简单的字符串拼接。标记模版彻底适用于其它场景:如函数调用。

上面的代码与这段等效:

var message =  
  SaferHTML(templateData, bonk.sender);

templateData 是由模版的字符串部分所组成的不可变数组,由JS引擎为咱们提供。下面是一个拥有两个元素的数组,由于在标记模板中他们被模版替换所分割,有两个字符串部分。因此templateData 应该是这个样子的:  Object.freeze (["<p>", " has sent you a bonk.</p>"]。

(实际上templateData 还有一个属性。我并不打算在这篇文章中用到它,为了完整性我仍是提一下: templateData.raw 是另外一个囊括了标记模版中全部字符串部分的数组,它的源码就和它的名称(raw)同样原始——还保留着\n这样的转义序列,而不是被转到新的一行等。标准的String.raw使用了最原始的字符串。)

这样就给了SaferHTML函数很大的自由度,能够用不少可选的方法去解析字符串和替换字符。

如今你必定想弄清楚SaferHTML到底是怎么实现的,或许你想亲手来尝试实现它。它终究只是个函数而已。你能够再Firefox的控制台中测试一下。

下面是其中一种方式(查看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(/</g, "<")  
            .replace(/>/g, ">");  
  
    // Don't escape special characters in the template.  
    s += templateData[i];  
  }  
  return s;  
}

定义了这个方法之后,SaferHTML`<p>${bonk.sender} has sent you a bonk.</p>`将会被解析为 "<p>ES6&lt;3er has sent you a bonk.</p>"。那么你的用户传入的值都是安全的,即便有人恶意地将bonk.sender 赋值为 "Hacker Steve <script>alert('xss');</script>",返回的也是不可执行的字符串,不管这段字符串的含义是什么,都不会有安全漏洞。

(顺便说一句,若是你以为函数使用参数对象的方式让你感受这样很笨重,咱们下次将换用另外一种方法。ES6还有另外一个我认为你会喜欢的新特性。)

一个例子并不能说明标记模版的灵活性。让咱们再来看看咱们能为以前列出的那些模版字符串的局限性作些什么改进。

  • 模版字符串不自动转义特殊字符。可是咱们已经看到了,使用标记模版咱们就能解决这个问题。实际上,咱们能作得更好。

从安全性的角度来看,个人SaferHTML函数功能性很是弱。HTML中不一样的地方有不一样的关键字须要不一样的转义方式。SaferHTML把它们所有都转义,可是咱们能够再付出一些努力把SaferHTML写的更精明一点,彻底按照字节来解析templateData中的字符串,这样咱们就知道哪些替换位是在纯HTML中;哪些是在元素属性中,这些 ‘ 和 “ 就须要转义;那些在URL查询字符串中须要使用URL转义而不是HTML转义等等。它能够准确地对每一个替代位进行正确的转义。

也许你会有疑问——HTML解析效率低,这方法是否是很牵强?幸运的是标记模版的字符串部分是固定不变的。SaferHTML能够将这部分的解析结果缓存起来,这样就能够提升运行速度了。(这个缓存能够是一个WeakMap,WeakMap是ES6的新特性,咱们在之后的文章中将会对它有所讨论。)

  • 模版字符串没有涉及国际化相关的特性。好在有了标记,这个问题就迎刃而解。Jack Hsu发表了一篇博客为咱们迈出了这第一步。下面是一个例子:

i18n`Hello ${name}, you have ${amount}:c(CAD) in your bank account.`  
// => Hallo Bob, Sie haben 1.234,56 $CA auf Ihrem Bankkonto.


这就是标记模板的做用了。

  • 它并不能代替像Mustache 、Nunjucks这样的模板库,大部分缘由是由于它没有内置对循环和条件句的支持。那么如今咱们一块儿来看看如何解决这个问题,准备好了吗?

若是JS没有为咱们提供某个特性,那咱们就本身写一个!

// Purely hypothetical template language based on  
// ES6 tagged templates.  
var libraryHtml = hashTemplate`  
  <ul>  
    #for book in ${myBooks}  
      <li><i>#{book.title}</i> by #{book.author}</li>  
    #end  
  </ul>  
`;

它的灵活性还不止于此。记住,标记函数的参数不会自动转换为字符串。它的返回值也是这样,它们能够是任何类型,标记模版不必定是字符串!你可使用自定义标签来建立自定义正则表达式、DOM树、图片、完整的异步流程、JS数据结构、GL着色器等等。

标记模版鼓励库开发人员去建立强大的基于特定领域的语言。这些语言看起来一点也不像JS,可是他们能够无缝地嵌入JS中而且能够智能地与其它语言进行交互。如今,我想不出其它任何有相似特性的语言,我不知道这个特性未来会给咱们的开发带来什么改变,但这个改变应该是使人惊喜的。

我何时能够开始使用模版字符串?

在服务端,io.js已经开始支持模版字符串了。

浏览器里,FireFox 34+已经支持模版字符串。去年夏天Guptha Rajagopal已经把它做为一个实习项目。Chrome 41+也支持模板字符串,IE和Safari不支持。如今,若是你想在web项目中使用模版字符串你须要用到BabelTraceur来支持;你也能够在TypeScript中使用。

在Markdown中可使用吗?

嗯? 

哦……这是一个好问题。

(这一部分与JavaScript无关。若是你不使用Markdown,能够跳过这一段。)

Markdown和JavaScript都使用 ` 来做为模版字符串的特殊字符。实际上,在Markdown中

它是内联文本的定界符。

若是你在Markdown中用下面的方式来写是会有问题的:

To display a message, write `alert(`hello world!`)`.

显示的结果为:

To display a message, write alert(hello world!).

请注意,在输出中是不会有小句号的。Markdown把这个四个小句号都解释为定界符,在输出结果中被HTML标记替换掉了。

为了不这种状况,咱们一开始在Markdown就采用了一种比较少见的特性:使用多重小句号来做为代码定界符,以下:

To display a message, write ``alert(`hello world!`)``.

能够到 Gist 上去查看更多细节,它是由Markdown写的,你能够查看源码。

下期预告

下篇文章,咱们将学习两个新特性,在其它语言中这两个特性已经被程序员们愉快地使用了几十年了:其中一个是为了那些喜欢尽可能避免使用参数的开发人员准备的,另外一个是给那些喜欢使用不少参数的人准备的。固然,两种特性都为咱们提供了,咱们能够根据我的对函数的使用习惯来选择。

这些特性能够直接在Firefox中测试,因此下次咱们一块儿来试一下吧,咱们的客座来宾Benjamin Peterson将为你们讲解ES6的default parameters和rest parameters。(译者:向渝 责编:陈秋歌)

原文连接:ES6 In Depth: Template strings

本译文遵循Creative Commons Attribution Share-Alike License v3.0 

相关文章
相关标签/搜索