JavaScript 模板字符串与标签模板:不只是拼接
掘金本周最热 · 软件开发
1. 引言:从 + 拼接说起 在 ES6 之前,我们拼接字符串基本靠 + : const name = "小明" ; const age = 18 ; const msg = "我叫" + name + ",今年" + age + "岁。" ; 这段代码能跑,但问题也很明显:引号、加号、空格混在一起,稍长一点就难以阅读;换行要写 \n ;变量多了更是灾难。 ES6 引入的**模板字符串(Template Literals)**用反引号 ` 包裹,一举解决了多行与插值两大痛点。而它的进阶形态—— 标签模板(Tagged Templates) ,则把「字符串拼接」升级成了「可编程的字符串处理」,成为安全转义、国际化、语法高亮等场景的底层基石。 本文从基础语法讲到标签模板的实战应用,帮你把这块内容彻底吃透。 先看一张全景图,理清本文的知识脉络: flowchart TD A["模板字符串 Template Literals"] --> B["多行字符串"] A --> C["插值 ${}"] A --> D["标签模板 Tagged Templates"] D --> E["strings 静态片段"] D --> F["values 插值结果"] E --> G["安全转义 safeHtml"] F --> G D --> H["国际化 i18n"] D --> I["语法高亮 highlight"] D --> J["DSL:styled-components / lit-html / gql"] 2. 多行字符串:告别 \n 模板字符串最直观的好处,就是 天然支持换行 ,写什么就是什么: const html = ` <div class="card"> <h2>标题</h2> <p>内容</p> </div> ` ; 对比传统写法: // 旧写法:需要手动拼接 \n,缩进也很别扭 var html = '<div class="card">\n' + ' <h2>标题</h2>\n' + ' <p>内容</p>\n' + '</div>' ; 需要注意两个细节: 缩进会被保留 。上面例子中每行前的空格会原样进入字符串,如果对格式敏感(比如生成 SQL、比较字符串),可以用 trim() 或专门的去缩进处理。 反引号需要转义 。字符串里出现 ` 时写成 \` ;出现 ${ 时写成 \${ ,否则会被当成插值。 3. 插值: ${} 里可以放任何表达式 ${} 中不仅能放变量,还能放任意 JavaScript 表达式: const price = 99 ; const count = 3 ; console . log ( `总价: ${price * count} 元` ); // 总价:297 元 console . log ( `是否包邮: ${price * count > 200 ? "是" : "否" } ` ); // 是否包邮:是 console . log ( `大写: ${ "hello" .toUpperCase()} ` ); // 大写:HELLO 甚至可以嵌套模板字符串: const users = [ "小明" , "小红" ]; const list = `用户列表: ${users.map(u => `<li> ${u} </li>` ).join( "" )} ` ; 一个常见误区 : ${} 里不要写语句(如 if 、 for ),只能写表达式。需要复杂逻辑时,先在外部算好再插入。 4. 标签函数:把模板变成函数调用 这是模板字符串真正「进阶」的地方。在模板字符串前加一个函数名,就构成了 标签模板 : function tag ( strings, ...values ) { console . log (strings); // 静态字符串片段数组 console . log (values); // 插值结果数组 return "返回值" ; } const name = "小明" ; const result = tag `你好, ${name} !欢迎回来。` ; 执行时, tag 收到的参数是: strings : ["你好,", "!欢迎回来。"] —— 被插值切开的 静态片段 values : ["小明"] —— 每个 ${} 求值后的结果 也就是说, strings.length === values.length + 1 恒成立。这个结构让标签函数可以 完全掌控 字符串的组装方式。 用一张图看清 tag 调用时字符串