一、概念
在 ES6 中,函数不仅可以通过传统方式 fn()
调用,还可以通过 标签模板(Tagged Template Literals) 的方式 fn
...`` 调用。
两种调用方式的核心差异是参数传入的方式:
fn()
:手动传参。fn
...``:由 JavaScript 自动将模板字面量分解为 静态字符串数组 和 动态表达式结果,并传递给函数。
二、原理
1. 普通函数调用
javascript
function add(a, b) {
return a + b;
}
console.log(add(2, 3)); // -> 5
👉 参数完全由调用方提供。
2. 标签模板函数调用
javascript
function tag(strings, ...values) {
console.log(strings); // 静态部分数组
console.log(values); // 插值表达式的计算结果
}
const name = "Alice";
const age = 20;
tag`Hi, my name is ${name} and next year I will be ${age + 1}`;
执行过程:
-
模板字面量
Hi, my name is ${name} and next year I will be ${age + 1}
-
JS 引擎分割为:
strings = ["Hi, my name is ", " and next year I will be ", ""]
values = ["Alice", 21]
-
调用函数:
tag(strings, "Alice", 21)
👉 这说明 模板中的 ${...}
可以是任意合法表达式,不仅仅是变量。
三、对比
特性 | fn() 调用 |
fn ...`` 调用 |
---|---|---|
参数来源 | 显式传入 | 模板字面量分解自动传入 |
动态表达式支持 | 需手动写表达式并传参 | 模板内部直接写 ${...} ,自动求值 |
可读性 | 适合逻辑函数 | 更适合描述文本/DSL 场景 |
典型应用 | 算法、业务逻辑 | SQL 构造、HTML 渲染、国际化、CSS-in-JS |
四、实践
示例 1:带表达式的 SQL 构造器
sql
function sql(strings, ...values) {
// 将插值安全转义(演示用,真实情况需更严格处理)
const safeValues = values.map(v => `'${v}'`);
// 拼接完整 SQL
return strings.reduce((result, str, i) => result + str + (safeValues[i] ?? ""), "");
}
const table = "users";
const minAge = 18;
const maxAge = 30;
// 模板中可直接放表达式:范围查询
const query = sql`SELECT * FROM ${table} WHERE age BETWEEN ${minAge} AND ${maxAge + 5}`;
console.log(query);
// 输出: SELECT * FROM 'users' WHERE age BETWEEN '18' AND '35'
👉 注意 ${maxAge + 5}
就是 表达式,计算后再传入函数。
示例 2:国际化 + 表达式
javascript
function i18n(strings, ...values) {
const dict = { "Hello": "你好", "years old": "岁" };
return strings.reduce((res, str, i) => {
const translated = dict[str.trim()] || str;
return res + translated + (values[i] ?? "");
}, "");
}
const user = { name: "小明", birth: 2000 };
const currentYear = 2025;
// 模板中写表达式:年龄 = 当前年份 - 出生年份
console.log(i18n`Hello ${user.name}, you are ${currentYear - user.birth} years old`);
// 输出: 你好 小明, you are 25 岁
👉 模板里的 ${currentYear - user.birth}
表达式先求值,再传给标签函数。
五、拓展
-
GraphQL
javascriptimport { gql } from "@apollo/client"; const query = gql` query { user(id: ${42}) { name } } `;
👉
gql
就是一个标签函数,接收模板片段并解析为 AST。 -
CSS-in-JS(styled-components)
iniconst Button = styled.button` background: ${props => props.primary ? "blue" : "gray"}; color: white; `;
👉 通过
${表达式}
可以根据组件属性动态生成 CSS。
六、潜在问题
- 表达式副作用
${func()}
内部函数若有副作用,模板调用时可能引发隐式执行。 - 调试困难
插值表达式复杂时,最终拼接结果可能难以直观理解。 - 性能注意
模板调用每次会构造新数组,若在循环内频繁使用可能带来 GC 压力。
总结
fn()
调用:传统传参,适合业务逻辑。fn
...`` 调用 :标签模板调用,自动传入 字面量片段数组 + 表达式求值结果 ,不仅能传变量,还能传 任意 JS 表达式。- 应用场景:SQL 构造、国际化、GraphQL、CSS-in-JS 等。