1

了解ES6中的模板字符串的标签函数

 2 years ago
source link: https://segmentfault.com/a/1190000041023016
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

模板字符串是可能是我们耳熟能详的一个ES6新特性,它可以允许我们在字符串中插入变量,还能够换行等等,确实使用起来非常地方便。然而,ES6还新增了一种主要用于和模板字符串配合使用的标签函数。

1.什么标签函数?

顾名思义,标签函数也是一种函数。这是一个很简单的标签函数声明:

function tagFoo() {
    console.log('这是一个标签函数');
}

仔细观察一下,看看tagFoo和我们常用的普通函数有什么区别呢?是不是看不出区别呢?得出这个结论很正常,因为标签函数其实就是一个函数,所以它的声明和函数声明是完全一样的。

那么,我们怎么知道tagFoo是一个标签函数呢,或者说标签函数相对于普通函数有什么特殊之处呢?答案是看tagFoo调用方式

tagFoo();//这是一个标签函数
tagFoo`一个模板字符串`;//这是一个标签函数

标签函数除了可以作为普通函数,通过()调用之外,还可以使用模板字符串``来调用。换句话说,当一个函数使用模板字符串的方式调用时, 这个函数就可以被称为标签函数,所以我们不妨把标签函数理解为新增的一种函数调用方式。

那么我们可能会想到,这两种调用方式有什么不同呢?它们的返回值是什么呢?

不论哪种调用方式,都是调用了这个函数,最终的返回值也都是执行这个函数之后的结果。我们给tagFoo加上一个返回值来看看:

function tagFoo() {
    return '我是返回值';
}

let res1 = tagFoo(); 
let res2 = tagFoo`一个模板字符串`; 
console.log({ res1, res2 });//{ res1: '我是返回值', res2: '我是返回值' }

2.标签函数的参数

excuse me?难道这两种调用方式就只有看起来不一样吗?当然不是。想来我们都已经注意到了,普通函数的调用方式允许我们自行传入参数,而标签函数调用方式似乎没有给我们提供传入参数的机会。一起看看标签函数存在参数时,它的参数是什么吧:

function tagFoo(...args) {
    console.log(...args);
}

tagFoo`一个普通的模板字符串`; // [ '一个普通的模板字符串' ]
tagFoo`一个有插值的模板字符串:${'var'}`; //[ '一个有插值的模板字符串:', '' ] var
tagFoo`一个有插值的模板字符串:${'var1'}-${'var2'}`; //[ '一个有插值的模板字符串:', '-', '' ] var1 var2

从上面可以看出,标签函数调用时,接收到的一个参数总是一个数组,数组中的元素就是模板字符串中的字符串部分;从第二个参数开始的剩余参数接收的是模板字符串的插值变量,这些变量的数目是任意的。换种方式声明的话,可能更直观一些:

function tagFoo(templateStrings, ...insertVars) {
    console.log({ templateStrings, insertVars });
}
tagFoo`一个普通的模板字符串`; //{ templateStrings: [ '一个普通的模板字符串' ], insertVars: [] }
tagFoo`一个有插值的模板字符串:${'var'}`; //{ templateStrings: [ '一个有插值的模板字符串:', '' ], insertVars: [ 'var' ] }
tagFoo`一个有插值的模板字符串:${'var1'},${'var2'}`; //{ templateStrings: [ '一个有插值的模板字符串:', '-', '' ], insertVars: [ 'var1', 'var2' ] }

也可以用一张图来表示:

也许可以形容为,templateStrings中的每两个元素之间,都应该有一个insertVars中插入的变量。两个数组中元素的顺序是有对应关系的。

3.标签函数有什么用?

标签函数让我们根据模板字符串进行自己的逻辑行为,让一些操作变得很简单。

举一个简单的例子,将模板字符串中的价格n转成$n

function $(templateStrings, ...insertVars) {
    return templateStrings.reduce((res, temp, i) => {
        return res + temp + (i >= insertVars.length ? '' : '$' + insertVars[i]);
    }, '');
}

console.log($`1号衣服原价${42},打折后的价格是${2}`); //1号衣服原价$42,打折后的价格是$2
console.log($`2号鞋子原价${58},打折后的价格是${88}`); //2号鞋子原价$58,打折后的价格是$88

不使用标签函数当然也能实现这个效果,也很简单:

function $(n) {
    return '$' + n;
}
console.log(`1号衣服原价${$(42)},打折后的价格是${$(2)}`); //1号衣服原价$42,打折后的价格是$2
console.log(`2号鞋子原价${$(58)},打折后的价格是${$(88)}`); //2号鞋子原价$58,打折后的价格是$88

使用哪种方式取决于我们自己的喜好。

但是,在处理一些特殊的情景时,标签函数可能会整一个大惊喜给到我们。

比如styled-components所做的:

const Button = styled.a`
  /* This renders the buttons above... Edit me! */
  display: inline-block;
  border-radius: 3px;
  padding: 0.5rem 0;
  margin: 0.5rem 1rem;
  width: 11rem;
  background: transparent;
  color: white;
  border: 2px solid white;

  /* The GitHub button is a primary button
   * edit this to target it specifically! */
  ${props => props.primary && css`
    background: white;
    color: black;
  `}
`

得到的Button就是一个React组件。通过styled-components,我们可以在JS中写css样式了!

我们也可以模仿styled-components,实现一个简单的styled.a

const styled = {
    a(stringProps, ...getProps) {
        const varProps = getProps.map((f) => f({}) || '');
        const style = stringProps
            .reduce((prop, stringProp, i) => {
                return (
                    prop +
                    stringProp +
                    (i >= varProps.length ? '' : varProps[i])
                );
            }, '')
            //删除注释和换行
            .replace(/(\n|(\/\*[\s\S]*?\*\/))/g, '')
            //删除空格
            .replace(
                /\s*?(?<propName>\w+):(?<propValue>[\s\S]*?);\s*/g,
                (...args) => {
                    const { propName, propValue } = args.pop();
                    return `${propName}:${propValue};`;
                }
            );

        //为了方便展示,返回一个字符串
        return `<a style="${style}"></a>`;
    },
};
const Button = styled.a`
    /* This renders the buttons above... Edit me! */
    display: inline-block;
    border-radius: 3px;
    padding: 0.5rem 0;
    margin: 0.5rem 1rem;
    width: 11rem;
    background: transparent;
    color: white;
    border: 2px solid white;

    /* The GitHub button is a primary button
   * edit this to target it specifically! */
    ${(props) => !props.primary && 'background: white;'}
`;

console.log(Button);
//<a style="display: inline-block;border-radius: 3px;padding: 0.5rem 0;margin: 0.5rem 1rem;width: 11rem;background: transparent;color: white;border: 2px solid white;background: white;"></a>

不得不说,标签函数在处理字符串的时候,真的很有吸引力。

当然,你会不会使用标签函数,还是取决于自己的喜好,萝卜白菜,各有所爱嘛。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK