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 等。
相关推荐
万少10 分钟前
我的HarmonyOS百宝箱
前端
江城开朗的豌豆17 分钟前
uni-app弹层遮罩难题?看我如何见招拆招!
前端·javascript·微信小程序
江城开朗的豌豆21 分钟前
小程序生命周期漫游指南:从诞生到落幕的完整旅程
前端·javascript·微信小程序
亿元程序员25 分钟前
100个Cocos实例之双摇杆(57/100)
前端
Mike_jia27 分钟前
Kaniko:无特权容器镜像构建的革命者
前端
欧阳码农28 分钟前
忍了一年多,我做了一个工具将文章一键发布到多个平台
前端·人工智能·后端
Hy行者勇哥30 分钟前
软件开发中前端页面、后台管理页面、后端、数据中台的关系与开发流程
前端
江城开朗的豌豆34 分钟前
跨平台开发实战:我的小程序双端(iOS、安卓)开发指南
前端·javascript·微信小程序
IT_陈寒44 分钟前
Python性能优化:5个让你的代码提速300%的NumPy高级技巧
前端·人工智能·后端
艾小码1 小时前
前端路由的秘密:手写一个迷你路由,看懂Hash和History的较量
前端·javascript