Skip to Content
JavaScript标签函数

前言

在看 Next.js 新特性时,看到处理表单方式介绍的时候,看到了这种使用 sql 的方法。

tagFn_1

后面就去了解了一下这是什么。

什么是标签函数?

JS 中有一类特殊的函数 —— (字符串)标签函数,标签函数是一种特殊的函数调用语法,它赋予开发人员处理模板字面量的能力,从而以更加灵活和控制的方式生成和操作字符串,也就是用于自定义模版字符串的处理逻辑。标签函数是 ES6 引入的,因此不用担心兼容性问题! 标签函数除了可以作为普通函数,通过 fn()调用之外,标签函数还可以直接跟在模版字符串前面使用模板字符串 fn“ 来调用。换句话说,当一个函数使用模板字符串的方式调用时,这个函数就可以被称为标签函数,所以我们不妨把标签函数理解为新增的一种函数调用方式。比如:

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

内置的标签函数

JS 中只有一个内置标签函数 —— String.raw ,用于获取模字符串的原始字符串形式,即:

  • 处理替换(例如替换${name}为变量实际的值)
  • 不处理转义序列(例如 \n)
String.raw
const name = "xh"; const s1 = `Hello \n ${name}`; const s2 = String.raw`Hello \n ${name}`; console.log(s1); console.log(s2);

tagFn_2

标签函数的参数

当标签函数存在参数时,它的参数是什么?

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

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

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

可以看出来,templateStrings 中的每两个元素之间,都应该有一个 insertVars 中插入的变量。两个数组中元素的顺序是有对应关系的。当 templateStrings 开头和结尾有 insertVars 时会有个空字符串。

自定义标签函数

实现简易 String.raw

function myRaw(strings, ...values) { let result = ""; for (let i = 0; i < strings.length; i++) { result += strings.raw[i] || strings[i]; if (i < values.length) { result += values[i]; } } return result; } console.log(`Hello \n world!`); console.log(String.raw`Hello \n world!`); console.log(myRaw`Hello \n world!`);

tagFn_3

标签函数的应用场景

1. 语法校验

比如,对于 HTML 字符串,可以使用标签函数来自动转义模板字符串中的特殊字符,以防止 XSS (跨站脚本攻击)。下面是一个代码示例:

function safeHtml(strings, ...values) { let result = strings[0]; for (let i = 1; i < strings.length; i++) { let val = String(values[i - 1]); result += val.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;"); result += strings[i]; } return result; } let userSuppliedInput = "<img src=x onerror=alert('XSS')>"; const result = safeHtml`<div>${userSuppliedInput}</div>`; // <div>&lt;img src=x onerror=alert('XSS')&gt;</div>

2. i18n(国际化和本地化)

标签函数可以用于处理模板字符串中的文本,使其根据用户的语言和地区进行适当的转换。

function translate(strings, ...values) { const lang = "en"; // 假设当前语言为英语 const translations = { Hello: "你好", world: "世界", }; let result = ""; strings.forEach((string, index) => { result += string; if (values[index] !== undefined) { result += translations[values[index]] || values[index]; } }); return result; } const greeting = translate`${"Hello"}, ${"world"}!`; console.log(greeting); // 输出: "你好, 世界!"

3. 特殊使用 (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; `} `;

tagFn_4

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

总结

标签函数属于 ES6 特性,所以整体兼容性很不错,当我们需要处理模版字符串时,可以考虑自定义一个标签函数。

tagFn_caniuse

Last updated on