了解ES6中的模板字符串的标签函数
模板字符串是可能是我们耳熟能详的一个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*?(?\w+):(?[\s\S]*?);
\s*/g,
(...args) => {
const { propName, propValue } = args.pop();
return `${propName}:${propValue};
`;
}
);
//为了方便展示,返回一个字符串
return ``;
},
};
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);
//
不得不说,标签函数在处理字符串的时候,真的很有吸引力。
【了解ES6中的模板字符串的标签函数】当然,你会不会使用标签函数,还是取决于自己的喜好,萝卜白菜,各有所爱嘛。
推荐阅读
- 热闹中的孤独
- JS中的各种宽高度定义及其应用
- 我们重新了解付费。
- 我眼中的佛系经纪人
- 《魔法科高中的劣等生》第26卷(Invasion篇)发售
- 拍照一年啦,如果你想了解我,那就请先看看这篇文章
- Android中的AES加密-下
- (二)ES6第一节变量(let|(二)ES6第一节变量(let,const)
- 六步搭建ES6语法环境
- 放下心中的偶像包袱吧