字符串插值

通过模板字面量等语法将变量或表达式的值嵌入字符串中的处理方式。

字符串插值 (String Interpolation) 是在字符串字面量中嵌入变量或表达式值的功能。它比字符串拼接 (使用 + 运算符连接) 具有更高的可读性,截至 2026 年时点,主要编程语言都具备某种形式的插值语法。从生成动态消息、支撑模板引擎的基础到格式化日志输出,所有需要动态构建文本的场景都会用到字符串插值。

在 JavaScript 中,使用模板字面量 (`Hello, ${name}!`) 进行字符串插值。用反引号包裹,${} 内可以写入任意表达式。不仅可以引用简单变量,还能嵌入函数调用、三元运算符和算术运算,使复杂的字符串组装在一行内完成。此外,使用标签模板字面量还可以对嵌入值施加自定义处理 (如 HTML 转义或国际化)。

在 Python 中,f-string (f"Hello, {name}!") 是 Python 3.6 以来的标准插值方式,在此之前使用的是 str.format() 和 % 运算符。Python 3.12 放宽了 f-string 的语法限制,现在可以在表达式中使用与外层相同的引号,也可以在表达式中包含反斜杠。Ruby 使用 "Hello, #{name}!",C# 使用 $"Hello, {name}!",Kotlin 使用 "Hello, $name!"。虽然各语言语法不同,但在字符串中直接嵌入变量的概念是通用的。Swift 的 "\(variable)" 和 PHP 的 "$variable" 也提供了相同的功能。

字符串插值与字符串拼接的取舍直接影响代码的可读性和可维护性。例如 "姓名: " + firstName + " " + lastName + " (年龄: " + age + ")" 这样的拼接,写成 `姓名: ${firstName} ${lastName} (年龄: ${age})` 的插值形式意图更加清晰。不过在多语言支持 (i18n) 的场合,情况恰好相反。模板字面量的嵌入位置被固定在代码一侧,因此无法按照语序不同的语言进行替换。要处理语序随语言变化的句子,需要使用在翻译目录一侧持有命名或编号占位符的格式指定型方式 (如 Python 的 str.format() 或 ICU MessageFormat),在运行时把值填入。像 "{name} は {city} に住んでいます" 与 "{name} lives in {city}" 这样,能够用相同的参数准备语序不同的译文,正是这种方式的优点。插值语法适用于在一处即可完结的消息,格式指定型适用于需要翻译的消息,据此区分使用。

字符串插值也需要注意安全问题。直接插值用户输入会成为 SQL 注入或 XSS 的原因,因此适当的转义处理和参数化查询不可或缺。EJS、Jinja2、Handlebars 等模板引擎大多具备自动转义功能,但需要注意 raw 或 safe 过滤器的误用。

从字符计数的角度来看,插值后字符串的长度取决于所嵌入值的字符数。插值数字时,toString() 结果的位数会直接反映为字符数。例如 ${price} 中放入 1000 是 4 个字符,放入 100000 则是 6 个字符;在需要固定长度格式的场合,与 padStart() 或 toFixed() 组合使用是有效的。

被插值的值也存在不符合预期位数的情况,在遵守字符数上限的设计中这会成为陷阱。在 JavaScript 中,由于二进制浮点数的误差,0.1 + 0.2 会成为 0.30000000000000004 这样的 19 个字符。1e21 以上的数值会切换为指数表示,1e+21 只有 5 个字符,而 1e20 仍然是 21 个字符。undefined 和 null 不会被嵌入为空字符串,而是分别作为 undefined 的 9 个字符和 null 的 4 个字符嵌入;直接传入对象则会成为 [object Object] 的 15 个字符。在必须遵守 API 的字段长度、数据库的列宽或输入框上限的场合,应在插值之前用 toFixed()、toLocaleString() 或明确指定默认值来确定写法,并对插值后的字符串进行长度判定。

分享这篇文章