资讯专栏INFORMATION COLUMN

ES6 系列之模板字符串

Travis / 2718人阅读

摘要:最终的代码如下第二版假设有这样一段为了保持可读性,我希望最终输入的样式为其实就是匹配每行前面的空格,然后将其替换为空字符串。

基础用法
let message = `Hello World`;
console.log(message);

如果你碰巧要在字符串中使用反撇号,你可以使用反斜杠转义:

let message = `Hello ` World`;
console.log(message);

值得一提的是,在模板字符串中,空格、缩进、换行都会被保留:

let message = `
    
  • 1
  • 2
`; console.log(message);

注意,打印的结果中第一行是一个换行,你可以使用 trim 函数消除换行:

let message = `
    
  • 1
  • 2
`.trim(); console.log(message);

嵌入变量

模板字符串支持嵌入变量,只需要将变量名写在 ${} 之中,其实不止变量,任意的 JavaScript 表达式都是可以的:

let x = 1, y = 2;
let message = `
  • ${x}
  • ${x + y}
`; console.log(message); //
  • 1
  • 3

值得一提的是,模板字符串支持嵌套:

let arr = [{value: 1}, {value: 2}];
let message = `
    
    ${arr.map((item) => { return `
  • ${item.value}
  • ` })}
`; console.log(message);

打印结果如下:

注意,在 li 标签中间多了一个逗号,这是因为当大括号中的值不是字符串时,会将其转为字符串,比如一个数组 [1, 2, 3] 就会被转为 1,2,3,逗号就是这样产生的。

如果你要消除这个逗号,你可以先 join 一下:

let arr = [{value: 1}, {value: 2}];
let message = `
    
    ${arr.map((item) => { return `
  • ${item.value}
  • ` }).join("")}
`; console.log(message);

打印结果如下:

标签模板

模板标签是一个非常重要的能力,模板字符串可以紧跟在一个函数名后面,该函数将被调用来处理这个模板字符串,举个例子:

let x = "Hi", y = "Kevin";
var res = message`${x}, I am ${y}`;
console.log(res);

我们可以自定义 message 函数来处理返回的字符串:

// literals 文字
// 注意在这个例子中 literals 的第一个元素和最后一个元素都是空字符串
function message(literals, value1, value2) {
    console.log(literals); // [ "", ", I am ", "" ]
    console.log(value1); // Hi
    console.log(value2); // Kevin
}

我们利用这些参数将其拼合回去:

function message(literals, ...values) {
    let result = "";

    for (let i = 0; i < values.length; i++) {
        result += literals[i];
        result += values[i];
    }

    result += literals[literals.length - 1];

    return result;
}

你也可以这样写:

function message(literals, ...values) {
    let result = literals.reduce((prev, next, i) => {
        let value = values[i - 1];
        return prev + value + next;
    });

    return result;
}

学着拼合回去是一件非常重要的事情,因为我们经过各种处理,最终都还是要拼回去的……

oneLine

讲完了基础,我们可以来看一些实际的需求:

let message = `
    Hi,
    Daisy!
    I am
    Kevin.
`;

出于可读性或者其他原因,我希望书写的时候是换行的,但是最终输出的字符是在一行,这就需要借助模板标签来实现了,我们尝试写一个这样的函数:

// oneLine 第一版
function oneLine(template, ...expressions) {
    let result = template.reduce((prev, next, i) => {
        let expression = expressions[i - 1];
        return prev + expression + next;
    });

    result = result.replace(/(s+)/g, " ");
    result = result.trim();

    return result;
}

实现原理很简单,拼合回去然后将多个空白符如换行符、空格等替换成一个空格。

使用如下:

let message = oneLine `
    Hi,
    Daisy!
    I am
    Kevin.
`;
console.log(message); // Hi, Daisy! I am Kevin.

不过你再用下去就会发现一个问题,如果字符间就包括多个空格呢?举个例子:

let message = oneLine`
  Preserve eg sentences.  Double
  spaces within input lines.
`;

如果使用这种匹配方式,sentences.Double 之间的两个空格也会被替换成一个空格。

我们可以再优化一下,我们想要的效果是将每行前面的多个空格替换成一个空格,其实应该匹配的是换行符以及换行符后面的多个空格,然后将其替换成一个空格,我们可以将正则改成:

result = result.replace(/(
s*)/g, " ");

就可以正确的匹配代码。最终的代码如下:

// oneLine 第二版
function oneLine(template, ...expressions) {
    let result = template.reduce((prev, next, i) => {
        let expression = expressions[i - 1];
        return prev + expression + next;
    });

    result = result.replace(/(
s*)/g, " ");
    result = result.trim();

    return result;
}
stripIndents

假设有这样一段 HTML:

let html = `
    1
    2
        3
`;

为了保持可读性,我希望最终输入的样式为:

1
2
3

其实就是匹配每行前面的空格,然后将其替换为空字符串。

// stripIndents 第一版
function stripIndents(template, ...expressions) {
    let result = template.reduce((prev, next, i) => {
        let expression = expressions[i - 1];
        return prev + expression + next;
    });


    result = result.replace(/
[^S
]*/g, "
");
    result = result.trim();

    return result;
}

最难的或许就是这个正则表达式了:

result = result.replace(/
[^S
]*/g, "
");

S 表示匹配一个非空白字符

[^S ] 表示匹配非空白字符换行符之外的字符,其实也就是空白字符去除换行符

[^S ]* 表示匹配换行符以及换行符后的多个不包含换行符的空白字符

replace(/ [^S ]*/g, " ") 表示将一个换行符以及换行符后的多个不包含换行符的空白字符替换成一个换行符,其实也就是将换行符后面的空白字符消掉的意思

其实吧,不用写的这么麻烦,我们还可以这样写:

result = result.replace(/^[^S
]+/gm, "");

看似简单了一点,之所以能这样写,是因为匹配模式的缘故,你会发现,这次除了匹配全局之外,这次我们还匹配了多行,m 标志用于指定多行输入字符串时应该被视为多个行,而且如果使用 m 标志,^ 和 $ 匹配的开始或结束是输入字符串中的每一行,而不是整个字符串的开始或结束。

1 表示匹配空白字符去除换行符

^2+ 表示匹配以去除换行符的空白字符为开头的一个或者多个字符

result.replace(/^3+/gm, "") 表示将每行开头一个或多个去除换行符的空白字符替换成空字符串,也同样达到了目的。

最终的代码如下:

// stripIndents 第二版
function stripIndents(template, ...expressions) {
    let result = template.reduce((prev, next, i) => {
        let expression = expressions[i - 1];
        return prev + expression + next;
    });


    result = result.replace(/^[^S
]+/gm, "");
    result = result.trim();

    return result;
}
stripIndent

注意,这次的 stripIndent 相比上面一节的标题少了一个字母 s,而我们想要实现的功能是:

let html = `
    
  • 1
  • 2
  • 3
    • `;

其实也就是去除第一行的换行以及每一行的部分缩进。

这个实现就稍微麻烦了一点,因为我们要计算出每一行到底要去除多少个空白字符。

实现的思路如下:

使用 match 函数,匹配每一行的空白字符,得到一个包含每一行空白字符的数组

数组遍历比较,得到最小的空白字符长度

构建一个正则表达式,然后每一行都替换掉最小长度的空白字符

实现的代码如下:

let html = `
    
  • 1
  • 2
  • 3
    • `; function stripIndent(template, ...expressions) { let result = template.reduce((prev, next, i) => { let expression = expressions[i - 1]; return prev + expression + next; }); const match = result.match(/^[^S ]*(?=S)/gm); console.log(match); // Array [ " ", " ", " ", " ", " " ] const indent = match && Math.min(...match.map(el => el.length)); console.log(indent); // 4 if (indent) { const regexp = new RegExp(`^.{${indent}}`, "gm"); console.log(regexp); // /^.{4}/gm result = result.replace(regexp, ""); } result = result.trim(); return result; }

值得一提的是,我们一般会以为正则中 . 表示匹配任意字符,其实是匹配除换行符之外的任何单个字符。

最终精简的代码如下:

function stripIndent(template, ...expressions) {
    let result = template.reduce((prev, next, i) => {
        let expression = expressions[i - 1];
        return prev + expression + next;
    });

    const match = result.match(/^[^S
]*(?=S)/gm);
    const indent = match && Math.min(...match.map(el => el.length));

    if (indent) {
        const regexp = new RegExp(`^.{${indent}}`, "gm");
        result =  result.replace(regexp, "");
    }

    result = result.trim();

    return result;
}
includeArrays

前面我们讲到为了避免 ${} 表达式中返回一个数组,自动转换会导致多个逗号的问题,需要每次都将数组最后再 join("") 一下,再看一遍例子:

let arr = [{value: 1}, {value: 2}];
let message = `
    
    ${arr.map((item) => { return `
  • ${item.value}
  • ` }).join("")}
`; console.log(message);

利用标签模板,我们可以轻松的解决这个问题:

function includeArrays(template, ...expressions) {
    let result = template.reduce((prev, next, i) => {

        let expression = expressions[i - 1];

        if (Array.isArray(expression)) {
            expression = expression.join("");
        }

        return prev + expression + next;
    });

    result = result.trim();

    return result;
}
最后

你会发现以上这些函数拼合的部分都是重复的,我们完全可以将其封装在一起,根据不同的配置实现不能的功能。如果你想在项目中使用这些函数,可以自己封装一个或者直接使用 common-tags。

ES6 系列

ES6 系列目录地址:https://github.com/mqyqingfen...。

ES6 系列预计写二十篇左右,旨在加深 ES6 部分知识点的理解,重点讲解块级作用域、标签模板、箭头函数、Symbol、Set、Map 以及 Promise 的模拟实现、模块加载方案、异步处理等内容。

如果有错误或者不严谨的地方,请务必给予指正,十分感谢。如果喜欢或者有所启发,欢迎star,对作者也是一种鼓励。

  • Sn ↩

  • Sn ↩

  • Sn ↩

  • 文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

    转载请注明本文地址:https://www.ucloud.cn/yun/107826.html

    相关文章

    • ES6 完全使用手册

      摘要:前言这里的泛指之后的新语法这里的完全是指本文会不断更新这里的使用是指本文会展示很多的使用场景这里的手册是指你可以参照本文将项目更多的重构为语法此外还要注意这里不一定就是正式进入规范的语法。 前言 这里的 ES6 泛指 ES5 之后的新语法 这里的 完全 是指本文会不断更新 这里的 使用 是指本文会展示很多 ES6 的使用场景 这里的 手册 是指你可以参照本文将项目更多的重构为 ES6...

      kgbook 评论0 收藏0
    • underscore 的源码该如何阅读?

      摘要:所以它与其他系列的文章并不冲突,完全可以在阅读完这个系列后,再跟着其他系列的文章接着学习。如何阅读我在写系列的时候,被问的最多的问题就是该怎么阅读源码我想简单聊一下自己的思路。感谢大家的阅读和支持,我是冴羽,下个系列再见啦 前言 别名:《underscore 系列 8 篇正式完结!》 介绍 underscore 系列是我写的第三个系列,前两个系列分别是 JavaScript 深入系列、...

      weknow619 评论0 收藏0
    • ES6系列文章 模板符串

      摘要:为我们提供了模板字符串,语法使用反引号。模板字符串具有以下三个优点多行文本字符串中插入变量字符串中插入表达式基本语法模板字符串和的字符串的声明一样。总结模板字符串就是这么的简单这么的好用。 ES6为我们提供了模板字符串,语法使用反引号`。模板字符串具有以下三个优点: 多行文本 字符串中插入变量 字符串中插入表达式 基本语法 模板字符串和 ES5的字符串的声明一样。 // ES5 v...

      woshicixide 评论0 收藏0
    • ES6 Features系列:Template Strings & Tagged Templ

      摘要:由两部分组成模板起始符,称为沉音符反引号,其内容被识别为字符串模板。其实这是通过属性操作中的结果,也就是说属性将对控制符进行转义从而实现按照普通字符输出。的语法是紧跟在后面,两者间不能有空格或制表符等。 1. Brief ES6(ECMAScript 6th edition)于2015年7月份发布,虽然各大浏览器仍未全面支持ES6,但我们可以在后端通过Node.js 0.12和io....

      MyFaith 评论0 收藏0
    • ES6语法符串的扩展

      摘要:模板字符串连接符在之前,将字符串连接到一起的旧方法是使用字符串连接运算符。这样更容易构建字符串。返回布尔值,表示参数字符串是否在原字符串的头部。它针对前个字符,而其他两个方法针对从第个位置直到字符串结束。 模板字符串 + 连接符 在 ES6 之前,将字符串连接到一起的旧方法是使用字符串连接运算符 (+)。 const student = { name: Richard Kaleho...

      孙淑建 评论0 收藏0

    发表评论

    0条评论

    Travis

    |高级讲师

    TA的文章

    阅读更多
    最新活动
    阅读需要支付1元查看
    <