• [ES6深度解析]4:Template strings(模板字符串)


    撇号基础知识(`)

    ES6引入了一种新的字符串字面量语法,称为模板字符串。它们看起来像普通的字符串,除了使用反勾字符ˋ而不是通常的引号'"。在最简单的情况下,它们实际上只是字符串:

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

    但这些字符串被称为“模板字符串”,而不是“没有任何特殊功能,只有反引号的乏味的普通字符串”是有原因的。模板字符串给JavaScript带来了简单的字符串插值。也就是说,它们是将JavaScript值插入字符串的一种漂亮、方便的方法。有无数种方法可以使用它,但最能温暖我心的是那句不起眼的错误信息:

    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.nameaction插入到结果字符串中。

    到目前为止,这只是+操作符的一个稍微更好的语法,下面的细节是你所期望的:

    • 模板替换中的代码可以是任何JavaScript表达式,因此允许函数调用、算术等。如果你愿意,甚至可以在另一个模板字符串中嵌套一个模板字符串,称之为模板初始化(template inception)
    • 如果一个值都不是字符串,它将使用通常的规则转换为字符串。例如,如果action是一个对象,它的.tostring()方法将被调用。
    • 如果你需要在模板字符串中写一个反勾号,你必须用反斜杠来转义它:ˋ"ˋ"相同。
    • 同样,如果你需要在模板字符串中包含两个字符${,你可以用反斜杠:${${转义字符。

    与普通字符串不同,模板字符串可以包含多行:

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

    模板字符串中的所有空格,包括换行符和缩进符,都将一字不差地包含在输出中。

    撇号的未来

    让我们来谈谈模板字符串不能做的一些事情:

    • 它们不会自动为您转义特殊字符。为了避免跨站点脚本漏洞(cross-site scripting),您仍然必须小心处理不可信的数据,就像连接普通字符串一样。
    • 模板字符串不处理数字和日期的特定语言格式,更不用说复数格式了。
    • 它们不是模板库的替代品,就像MustacheNunjucks
    • 模板字符串没有任何用于循环的内置语法——例如,从数组中构建HTML表table的行<tr>——甚至没有条件语句。

    标记模板(tagged templates)

    ES6在模板字符串上又做了一个改进,让JS开发人员和库设计人员能够解决这些限制等等。该特性称为标记模板(tagged templates)。

    标记模板的语法很简单。它们只是模板字符串,在开始的反引号之前有一个额外的标签。对于我们的第一个例子,标签将是SaferHTML,我们将使用这个标签来尝试解决上面列出的第一个限制:自动转义特殊字符。

    注意,SaferHTML不是由ES6标准库提供的。我们将在下面自己实现它。

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

    这里的标记是单一标识符SaferHTML,但标记也可以是一个属性,如SaferHTML.escape,甚至是一个方法调用,如SaferHTML.escape({unicodeControlCharacters: false})。(更准确地说,任何ES6的MemberExpression或CallExpression都可以作为标记。)

    我们看到,不带标记模板字符串是简单字符串连接的简写形式。带标记的模板是其他东西的简写:函数调用

    上面的代码相当于:

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

    其中templateData是模板中所有字符串部分的不可变数组,由JS引擎创建。在这里,数组将有两个元素,因为在标记模板中有两个字符串部分,由,分隔。所以templateData会像Object.freeze(["<p>"," has sent you a bonk.</p>"]

    实际上在templateData上还有一个属性。我们不会在本文中使用它,但是为了完整起见,我将提到它:templateData.raw是另一个数组,包含了带标签的模板中的所有字符串部分,但这一次与它们在源代码中看到的完全一样——像 这样的转义序列保持完整,而不是被转换成换行符等等。标准标记String.raw使用这些原始字符串。

    这使得SaferHTML函数可以自由地以百万种可能的方式解释字符串和替换。

    在继续阅读之前,您可能想要尝试弄清楚SaferHTML应该做什么,然后尝试自己实现它。毕竟,它只是一个函数。这里有一个可能的答案:

    function SaferHTML(templateData) {
      var s = templateData[0]; //templateData是字符串部分
      for (var i = 1; i < arguments.length; i++) {//arguments从第二个参数开始,记录的是模板字符串的插值
        var arg = String(arguments[i]);
    
        // 转义所有的插值
        s += arg.replace(/&/g, "&amp;")
                .replace(/</g, "&lt;")
                .replace(/>/g, "&gt;");
    
        // 不转义模板字符串的其他部分(除了插值)
        s += templateData[i];
      }
      return s;
    }
    

    单个示例不足以说明标记模板的灵活性。让我们回顾一下之前的模板字符串限制列表,看看还可以做些什么:

    • 模板字符串不会自动转义特殊字符。但是正如我们所看到的,有了标记模板,您可以用标记自己解决这个问题。事实上,你可以做得更好。从安全角度来看,我的SaferHTML函数非常弱。HTML中的不同位置有不同的特殊字符,需要以不同的方式转义;SaferHTML并没有对它们全部进行转义。但是通过一些努力,你可以编写一个更智能的SaferHTML函数,它实际解析templateData字符串中的HTML位置,这样它就知道哪些替换是在普通HTML中;哪些是元素属性内部的,因此需要转义'";哪些是在URL查询字符串中,因此需要URL转义而不是html转义;等等。它可以对每个需要替换的字符串执行正确的转义。这听起来是不是有些牵强,因为HTML解析很慢?幸运的是,当再次计算模板时,已标记模板的字符串部分不会更改。SaferHTML可以缓存所有这些解析的结果,以加快以后的调用。
    • 模板字符串没有内置的国际化特性。但是有了标签,我们可以添加它们。注意,在下面的这个例子中,nameamount是JavaScript的变量,但是有一个不熟悉的代码:c(CAD),把它放在了模板的字符串部分。JavaScript当然是由JavaScript引擎处理的;字符串部分由i18n标签函数处理。用户可以从i18n文档中了解到:c(CAD)表示金额是货币的数量,以加元计价。
      这就是标记模板的作用。
    i18n`Hello ${name}, you have ${amount}:c(CAD) in your bank account.`
    // => Hallo Bob, Sie haben 1.234,56 $CA auf Ihrem Bankkonto.
    
    • 模板字符串不能替代MustacheNunjucks,部分原因是它们没有用于循环或条件句的内置语法。但现在我们开始考虑如何解决这个问题了,对吧?如果JS不提供该特性,那么编写一个提供该特性的标记。
    // 纯粹假设的模板语言(基于ES6的标记模板)
    var libraryHtml = hashTemplate`
      <ul>
        #for book in ${myBooks}
          <li><i>#{book.title}</i> by #{book.author}</li>
        #end
      </ul>
    `;
    

    灵活性并不止于此。注意,标签函数的参数不会自动转换为字符串。它们可以是任何东西。返回值也是如此。带标签的模板甚至不一定是字符串!你可以使用自定义标签来创建正则表达式、DOM树、图像、代表整个异步进程的承诺、JS数据结构、GL着色器……

    本文来自博客园,作者:Max力出奇迹,转载请注明原文链接:https://www.cnblogs.com/welody/p/15167427.html

    如果觉得文章不错,欢迎点击推荐

  • 相关阅读:
    Django基础篇
    知识梳理
    其他类题目
    CDN原理
    OpenStack
    云计算三种服务模式SaaS、PaaS和IaaS
    高并发架构
    Andrid Studio Gradle sync failed: A problem occurred configuring project ':app' 解决方法
    Android Studio 创建项目后“Cannot resolve symbol” 解决办法
    阅读之推荐系统
  • 原文地址:https://www.cnblogs.com/welody/p/15167427.html
Copyright © 2020-2023  润新知