ES6 中函数的双重调用方式:fn() 与 fn\...``

一、概念

在 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}`;

执行过程:

  1. 模板字面量 Hi, my name is ${name} and next year I will be ${age + 1}

  2. JS 引擎分割为:

    • strings = ["Hi, my name is ", " and next year I will be ", ""]
    • values = ["Alice", 21]
  3. 调用函数: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} 表达式先求值,再传给标签函数。


五、拓展

  1. GraphQL

    javascript 复制代码
    import { gql } from "@apollo/client";
    const query = gql`
      query {
        user(id: ${42}) {
          name
        }
      }
    `;

    👉 gql 就是一个标签函数,接收模板片段并解析为 AST。

  2. CSS-in-JS(styled-components)

    ini 复制代码
    const Button = styled.button`
      background: ${props => props.primary ? "blue" : "gray"};
      color: white;
    `;

    👉 通过 ${表达式} 可以根据组件属性动态生成 CSS。


六、潜在问题

  1. 表达式副作用
    ${func()} 内部函数若有副作用,模板调用时可能引发隐式执行。
  2. 调试困难
    插值表达式复杂时,最终拼接结果可能难以直观理解。
  3. 性能注意
    模板调用每次会构造新数组,若在循环内频繁使用可能带来 GC 压力。

总结

  • fn() 调用:传统传参,适合业务逻辑。
  • fn...`` 调用 :标签模板调用,自动传入 字面量片段数组 + 表达式求值结果 ,不仅能传变量,还能传 任意 JS 表达式
  • 应用场景:SQL 构造、国际化、GraphQL、CSS-in-JS 等。
相关推荐
崔庆才丨静觅3 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60614 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了4 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅4 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅4 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅5 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment5 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅5 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊5 小时前
jwt介绍
前端
爱敲代码的小鱼5 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax