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 等。
相关推荐
问商十三载23 分钟前
RAG的适用边界在哪里?2026年价值与局限详解
大数据·前端·人工智能
爱勇宝1 小时前
人生没有最好的年龄,只有最好的状态
前端
幼儿园技术家1 小时前
前端 Node 全家桶
前端·js or ts
无人生还1 小时前
从 Vue3 到 React · 快速上手系列第 9 篇:自定义 Hook(对标 Composables)
前端·vue.js·react.js
张龙6871 小时前
RAG 知识库问答系统实战:分块、混合检索、RRF 融合与重排全链路拆解
前端
用户1306095607231 小时前
第二章学完后,我对 webpack5 前端工程化的一点理解
前端
a1117761 小时前
小鸡下蛋 小游戏 html
前端·开源·html
码哥DFS2 小时前
算法练习day1-备战2027届秋招
前端·javascript·数据结构·算法
DevOpenClub2 小时前
用网页快照、静态 HTML 和 PDF 转换接口构建网页归档 Agent
前端·人工智能·pdf·html
Bigger2 小时前
因为一句「华流才是最屌的」,我做了一个中国风设计的 Skill
前端·人工智能·设计