Maison  >  Article  >  interface Web  >  Explication détaillée des chaînes de modèles en JavaScript ES6_Basic knowledge

Explication détaillée des chaînes de modèles en JavaScript ES6_Basic knowledge

WBOY
WBOYoriginal
2016-05-16 15:48:411436parcourir

Un nouveau type de chaîne littérale est introduit dans ES6 - les chaînes de modèle À l'exception de l'utilisation de backticks (`), elles ressemblent aux chaînes ordinaires. Dans le cas le plus simple, ce ne sont que des chaînes ordinaires :

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

est appelé chaîne de modèle car la chaîne de modèle introduit une fonctionnalité d'interpolation de chaîne simple dans JS, c'est-à-dire que la valeur JS peut être insérée de manière pratique et élégante dans une chaîne.

Les chaînes de modèles peuvent être utilisées à de nombreux endroits, regardez le message d'erreur discret suivant :

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

Dans le code ci-dessus, ${user.name} et ${action} sont appelés espaces réservés de modèle. JavaScript insérera respectivement les valeurs de user.name et d'action dans les positions correspondantes, puis générera "User". " comme ça, Jorendorff n'est pas autorisé à jouer au hockey. "

Maintenant que nous disposons d'une syntaxe plus élégante que l'opérateur, voici quelques fonctionnalités auxquelles vous pouvez vous attendre :

L'espace réservé au modèle peut être n'importe quelle expression JavaScript, donc les appels de fonction et quatre opérations arithmétiques sont légaux. (Vous pouvez même imbriquer une chaîne de modèle dans une autre chaîne de modèle.)
Si une valeur n'est pas une chaîne, elle sera convertie en chaîne. Par exemple, si l'action est un objet, alors le .toString() de cet objet sera appelé pour le convertir en chaîne.
Si vous souhaitez utiliser des backticks dans une chaîne de modèle, vous devez les échapper avec une barre oblique inverse.
De même, si vous souhaitez afficher ${ dans la chaîne du modèle, vous devez également utiliser des barres obliques inverses pour l'échapper : ${ ou ${.
Les chaînes de modèle peuvent s'étendre sur plusieurs lignes :

$("#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>
`);

Tous les espaces, nouvelles lignes et retraits de la chaîne du modèle seront affichés tels quels dans la chaîne de résultat.

Jetons un coup d'œil à ce que les chaînes de modèles ne peuvent pas faire :

Les caractères spéciaux ne seront pas automatiquement échappés. Afin d'éviter les vulnérabilités de cross-site scripting, vous devez toujours être prudent avec les données non fiables, tout comme les chaînes ordinaires.
Il ne peut pas être utilisé avec des bibliothèques internationales et ne gère pas les nombres, les dates, etc. dans des formats de langage spéciaux.
Ne remplace pas les moteurs de modèles (tels que Moustache ou Nunjucks). Les chaînes de modèles n'ont pas de syntaxe pour gérer les boucles : vous ne pouvez pas créer une table à partir d'un tableau.

Pour remédier à ces limitations, ES6 fournit aux développeurs et aux concepteurs de bibliothèques un autre type de chaîne de modèle : les modèles de balises.

La syntaxe du modèle de balise

est très simple, il vous suffit d'introduire une balise avant le backtick de départ. En regardant le premier exemple : SaferHTML, nous allons utiliser ce modèle de balise pour résoudre la première limitation mentionnée ci-dessus : l'échappement automatique des caractères spéciaux.

A noter que la méthode SaferHTML n'est pas fournie par la bibliothèque standard ES6, nous devons l'implémenter nous-mêmes :

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

La balise SaferHTML est ici un identifiant unique, et la balise peut également être un attribut, comme SaferHTML.escape, ou même un appel de méthode : SaferHTML.escape({unicodeControlCharacters: false}). Pour être précis, n'importe quelle expression membre ES6 ou expression d'appel peut être utilisée comme balise.

On peut voir que la chaîne de modèle n'est qu'un sucre syntaxique pour la concaténation de chaînes, alors que le modèle d'étiquette est en effet une chose complètement différente : un appel de fonction.

Donc, le code ci-dessus est équivalent à :

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

Où templateData est un tableau de chaînes immuable, généré par le moteur JS basé sur la chaîne du modèle source. Le tableau contient ici deux éléments, car la chaîne du modèle contient deux chaînes après avoir été séparées par des espaces réservés. Par conséquent, templateData sera comme ceci. : Object.freeze(["e388a4556c0f65e1904146cc1a846bee", " vous a envoyé un bonk.94b3e26ee717c64999d7867364b1b4a3"]

(En fait, il existe un autre attribut sur templateData : templateData.raw. Cet article ne traite pas de cet attribut en profondeur. La valeur de cet attribut est également un tableau, comprenant toutes les parties de chaîne du modèle de balise, mais le string Contient des séquences d'échappement qui ressemblent davantage à des chaînes dans le code source, telles que n. La balise intégrée ES6 String.raw utilisera ces chaînes )

.

Cela permet à la méthode SaferHTML d'analyser ces deux chaînes à volonté, et il existe N méthodes de remplacement.

En poursuivant votre lecture, vous vous demandez peut-être comment mettre en œuvre la méthode SaferHTML.

Voici une implémentation (essentiel) :

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 中,并且可以与语言的其余部分进行交互。顺便说一下,我还没有在其他语言中见过类似的特性,我不知道这个特性讲给我们带来些什么,但各种可能性还是非常令人兴奋的。

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn