title: 前端开发系列127-进阶篇之tagged template
tags:
categories: []
date: 2019-08-13 18:32:08
本文介绍模板字符串的某种特殊用法,允许我们在函数名后跟一个模板字符串,然后可以像正常函数调用一样来把模板字符串中的各个部分以参数的方式传递给该函数。

标签模板-tagged template 并非真正的字符串模板,而是一种特殊形式的函数调用。

在标签模板中的标签(tag)指的是函数,而跟在其后的模板表示的则是函数调用时传递的实际参数。假如我们存在一个函数 function tag(){console.log(arguments)},那么当在tag后跟上模板字符串的时候就会变成函数调用。

function tag() {
    console.log(arguments);
}

let name = "Yong";
let age = 18;
tag `My name is ${name}. I'm ${age} years old`;

/* 打印输出 */
/* 
[Arguments] {
  '0': [ 'My name is ', '. I\'m ', ' years old' ],
  '1': 'Yong',
  '2': 18 }
*/

观察上面的代码和对应的执行结果,在 tag 函数 的调用过程中, 模板字符串部分My name is ${name}. I'm ${age} years old 被拆分成了不同的部分以参数形式传递到函数内容,具体在拆分的时候,所有非变量型(变量型指的是${name}这样的结构)的部分将被作为第一个参数传入,而所有变量型的数据则会依次跟在后面以第二、第三、第四... 参数的形式传入。

为了更方便的区分和处理这两部分参数,我们尝试通过剩余参数的方式来调整tag 函数的写法。

function tag(stringArr, ...args) {
    console.log("第一部分:", stringArr);
    console.log("第二部分:", args);
}

let name = "Yong";
let age = 18;
tag `My name is ${name}. I'm ${age} years old`;

/* 打印输出 */
/* 
第一部分: [ 'My name is ', '. I\'m ', ' years old' ]
第二部分: [ 'Yong', 18 ]
*/

字符串双反引号的写法( 我们通常称为模板字符串 )是ES6为我们提供的新特性,作为增强版本的字符串,这个特性至少为我们带来了两点便利。

1、方便的处理字符串中的换行。
2、方便的在字符串中以${ }的形式来插入变量,以替代原本的 + 来完成拼接。

备注:在模板字符串的 ${} 中可以放任意的JavaScript表达式,譬如变量、普通字符串、简单计算、引用对象属性甚至是函数调用,具体可以参考下面的演示代码。

/* 1.插入变量 */
let address = "广州";
let str1 = `地址:${ address }`;

/* 2.插入数字 */
let str2 = `编号:${ 10086 }`;

/* 3.插入字符串 */
let str3 = `描述:${ 'description' }`;

/* 4.简单计算 */
let x = 1,
    y = 5;
let str4 = `计算:${x} + ${y} * 2 == ${x + y * 2}`;

/* 5.函数调用 */
let callFn = () => "callFn be Call";
let str5 = `函数调用:${callFn() }`;

/* 6.测试三元运算符 */
let str6 = `三元运算符:${ 5>1 ?'结果大于1':'结果不大于1'}`;

/* 打印输出 */
console.log(str1);
console.log(str2);
console.log(str3);
console.log(str4);
console.log(str5);
console.log(str6);
/* 
地址:广州
编号:10086
描述:description
计算:1 + 5 * 2 == 11
函数调用:callFn be Call
三元运算符:结果大于1 */

理所应当 && 自然而然,我们不假思索就用上了模板字符串这样牛逼的特性,不过我建议再多一层对反引号字符串的处理过程的思考,下面的代码中封装了一个函数来模拟和还原处理的过程。

function parse(strArr, ...args) {

    /* 打印参数 */
    console.log('strArr = ', strArr);
    console.log('args   = ', args);

    let output = "";

    args.forEach((s, idx) => {
        console.log(`current - strArr[${idx}]`, strArr[idx]);
        output += strArr[idx] + s;
    })

    /* 因为 args {变量参数}的个数总是会比strArr的数量多1,因此还需拼接末尾 */
    output += strArr[strArr.length - 1];

    return output;
}


let a = 1, b = 2;

/* 测试代码*/
console.log(parse `a = ${a} , b = ${b} , a + b = ${a + b} .`);

/* 打印输出  
strArr =  [ 'a = ', ' , b = ', ' , a + b = ', ' .' ]
args   =  [ 1, 2, 3 ]
current - strArr[0] a =
current - strArr[1]  , b =
current - strArr[2]  , a + b =
a = 1 , b = 2 , a + b = 3 .  
*/

在上面的代码中,我们通过标签模板的调用结构,利用parse 函数完成了模板字符串的"还原"工作。