一、概念
在 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 等。