模板字符串_ES6笔记3

一.作用及语法

模板字符串用来实现字符串插值,比加号(+)拼接更方便更优雅

`${expr}`语法,提供了字符串插值功能

注意:上面的`(反撇号,位于键盘Esc下面)是模板字符串语法的一部分,而不是markdown用错了

P.S.笔者遇到了第一个问题,反撇号与markdown语法有冲突,有两种选择,要么使用markdown特性之多行反撇好作为代码分隔符(用两个连续反撇号包裹代码,但笔者使用的markdown插件不支持该罕见语法),要么使用code标签,比如上面的markdown源码是<code>\`${expr}\`</code>

例如:

function req(user, action) {
    // if !isValid
    error(`user ${user} failed to do action ${action}`);
}
function error(str) {
    console.log(str);
}
// test
req('Sam', 'login');    // user Sam failed to do action login

二.特点

1.expr可以是任意合法js表达式,且支持嵌套

包括变量、字面量、函数调用等等,但不支持流程控制(分支/循环),所以模板字符串不能代替模板引擎

例如:

// 不支持分支/循环
// console.log(`${if (2 > 1) {str}}`);  // Uncaught SyntaxError: Unexpected token if
// console.log(`${for (; false;) {}}`); // Uncaught SyntaxError: Unexpected token for

var outer = 'outer';
var inner = 'inner';
var str = `
\$\{
    ${
        outer   // 变量
        +       // +拼接
        `\$\{
            ${inner}    // 插值
        \}`
    }
\}
`;

var h5 = document.querySelector('#h5');
var pre = document.querySelector('#pre');
h5.innerHTML = str;
pre.innerHTML = str;

显示结果如下:

// #h5
${ outer${ inner // 插值 } }
// #pre
${
    outer${
            inner    // 插值
        }
}

3.可以分多行书写

但注释(单行/多行)可能会作用于整个串,或者直接作为字面量出现在结果串里。模板字符串中所有的空格、新行、缩进,都会原样输出在生成的字符串中(pre与其它元素不同),具体见上例

4.特殊字符需要转义

3个特殊字符[`${](正则表达式形式)需要转义,具体见上例

P.S.右花括号不需要转义,当然,转义它也没错。但是上面3个必须转义

5.没有自动处理

不会自动过滤不安全标签,也不会自动转义特殊字符,同样需要防止xss攻击,虽然提供了标签模板(tagged templates),但不很方便

例如:

// xss攻击
var userInput = '<a href="" onclick="alert(\'xss attack\');">领取奖励</a>';
var input = document.querySelector('#input');
input.innerHTML = `用户输入:${userInput}`;

上面代码会生成一个链接,点击alert “xss attack”,通过标签模板可以对模板字符串进行自定义处理,比如滤掉不安全字符,如下:

// 定义模板标签
function myFilter(templateData) {
    var aStr = templateData;        // 被${}插值分割后的字符串数组
    var aRaw = templateData.raw;    // 未经转义的原字符串(\n)
    var aVar = Array.prototype.slice.call(arguments, 1);    // 插值变量数组
    console.log(aStr);
    console.log(aRaw);
    console.log(aVar);

    // filter vars
    aVar.forEach(function(item, index, arr) {
        arr[index] = item.replace(/(<[^>]*>)/i, '');
    });
    var res = '';
    var i = 0;
    aStr.forEach(function(item) {
        res += item;
        if (aVar[i]) {
            res += aVar[i++];
        }
    });

    return res;
}

// 使用标签模板
var safeInput = document.querySelector('#safe_input');
safeInput.innerHTML = myFilter`用户输入:\n${userInput}`;
// 等价于
// safeInput.innerHTML = myFilter(['用户输入:'], userInput);

标签出现在模板字符串前,是一种简化的函数调用,模板标签类似于回调函数,参数templateData的格式是确定的,从参数中取出数据,处理完毕返回即可

但定义模板标签感觉还是比较麻烦,但这是一个很有吸引力的特性,预示着一个强大的字符串处理库(转义特殊字符、i18n、字数统计等等)

6.灵活性

标签模板的返回值不一定是字符串,可以是任何值。这意味着不仅仅会出现一个强大的“字符串”处理库,任何库都可以以这种形式完美嵌入JS内,尤其是着色器语言这样的目前在JS中形式不太友好的东西……其它语言出现诱人的特性后,可以完美植入JS,甚至可以借此实现一种自己的语言(模板标签的函数体就是解释器)

标签模板带来很大的灵活性,可以用自定义的标签来创建正则表达式、DOM树、图片、以promises为代表的整个异步过程、JS数据结构、GL着色器……

引用参考资料中的原文:

标签模板以开放的姿态欢迎库设计者们来创建强有力领域特定语言。这些语言可能看起来不像JS,但是它们仍可以无缝嵌入到JS中并与JS的其它语言特性智能交互。我不知道这一特性将会带领们走向何方,但它蕴藏着无限的可能性,这令我感到异常兴奋!

三.总结

模板字符串看似语法糖,其实是打开了一扇大门,表示JS以开放的姿态迎接未来

还记得前辈们5年前讨论的yield吗?那时候他们多希望有个这样的特性啊,讨论这个问题的初衷不就是希望JS能获得C#的yield特性吗?

参考资料

  • 《ES6 in Depth》:InfoQ中文站提供的免费电子书

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*

code