Home  >  Article  >  Web Front-end  >  Detailed explanation of template strings in JavaScript ES6_Basic knowledge

Detailed explanation of template strings in JavaScript ES6_Basic knowledge

WBOY
WBOYOriginal
2016-05-16 15:48:411472browse

A new type of string literal is introduced in ES6 - template strings. Except for the use of backticks (`), they look the same as ordinary strings. In the simplest case, they are just ordinary strings:

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

is called template string because template string introduces simple string interpolation feature to JS, that is to say, JS value can be conveniently and elegantly inserted into a string.

Template strings can be used in many places, look at the following inconspicuous error message:

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}.`);
 }
}

In the above code, ${user.name} and ${action} are called template placeholders. JavaScript will insert the values ​​​​of user.name and action into the corresponding positions respectively, and then generate "User" like this jorendorff is not authorized to do hockey." string.

Now that we have a more elegant syntax than the operator, here are some features you can expect:

Template placeholder can be any JavaScript expression, so function calls and four arithmetic operations are legal. (You can even nest a template string within another template string.)
If a value is not a string, it will be converted to a string. For example, if action is an object, then that object's .toString() will be called to convert it to a string.
If you want to use backticks in a template string, you need to escape them with a backslash.
Similarly, if you want to output ${ in the template string, you also need to use backslashes to escape it: ${ or ${.
Template strings can span multiple lines:

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

All spaces, newlines and indents in the template string will be output to the result string as is.

Let’s take a look at what template strings can’t do:

Special characters will not be automatically escaped. In order to avoid cross-site scripting vulnerabilities, you still need to be careful with untrusted data, just like ordinary strings.
It cannot be used with international libraries and does not handle numbers, dates, etc. in special language formats.
Not a replacement for template engines (such as Mustache or Nunjucks). Template strings have no syntax for handling loops — you can't build a table from an array.

To address these limitations, ES6 provides developers and library designers with another type of template string — tag templates.

The syntax of

tag template is very simple, you only need to introduce a tag before the starting backtick. Looking at the first example: SaferHTML, we are going to use this tag template to solve the first limitation mentioned above: automatic escaping of special characters.

It should be noted that the SaferHTML method is not provided by the ES6 standard library, we need to implement it ourselves:

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

The SaferHTML tag here is a single identifier, and the tag can also be an attribute, such as SaferHTML.escape, or even a method call: SaferHTML.escape({unicodeControlCharacters: false}). To be precise, any ES6 member expression or call expression can be used as a tag.

It can be seen that the template string is just syntactic sugar for string concatenation, while the label template is indeed a completely different thing: a function call.

So, the above code is equivalent to:

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

Where templateData is an immutable string array, generated by the JS engine based on the source template string. The array here contains two elements, because the template string contains two strings after being separated by placeholders. Therefore, templateData will be like this: Object.freeze(["e388a4556c0f65e1904146cc1a846bee", " has sent you a bonk.94b3e26ee717c64999d7867364b1b4a3"]

(In fact, there is another attribute on templateData: templateData.raw. This article does not discuss this attribute in depth. The value of this attribute is also an array, including all the string parts in the tag template, but the string Contains escape sequences that look more like strings in the source code, such as n. The ES6 built-in tag String.raw will use these strings )

.

This allows the SaferHTML method to parse these two strings at will, and there are N replacement methods.

As you continue reading, you may be wondering how to implement the SaferHTML method.

Here is an implementation (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;
}
 
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 这个方法写的更好。从安全角度来看,这个 SaferHTML 非常脆弱。在 HTML 中,不同的地方需要用不同的方式去转义,SaferHTML 并没有做到。稍加思考,我们就可以实现一个更加灵活的 SaferHTML方法,能够将 templateData 中的任何一个 HTML 转义,知道哪个占位符是纯 HTML;哪个是元素的属性,从而需要对 ' 和 " 转义;哪个是 URL 的 query 字符串,从而需要用 URL 的 escaping 方法,而不是 HTML 的 escaping;等等。这似乎有些牵强,因为 HTML 转义效率比较低。辛运是的,标签模板的字符串是保持不变的,SaferHTML 可以缓存已经转义过的字符串,从而提高效率。
    模板字符串并没有内置的国际化特性,但通过标签模板,我们可以添加该特性。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.




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`
 <ul>
  #for book in ${myBooks}
   <li><i>#{book.title}</i> by #{book.author}</li>
  #end
 </ul>
`;

 
// 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 树、图片、代表整个异步进程的 Promise、JS 数据结构、GL 着色器…

标签模板允许库设计者创建强大的领域特定语言。这些语言可能看上去并不像 JS,但他们可以无缝嵌入到 JS 中,并且可以与语言的其余部分进行交互。顺便说一下,我还没有在其他语言中见过类似的特性,我不知道这个特性讲给我们带来些什么,但各种可能性还是非常令人兴奋的。

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn