如何在JavaScript中进行字符串插值?

796

考虑以下代码:

var age = 3;

console.log("I'm " + age + " years old!");

除了使用字符串拼接,还有其他方式将变量的值插入到字符串中吗?


7
你可以查看 CoffeeScript:http://coffeescript.org/。 - dennismonsewicz
2
正如其他人所指出的那样,你正在使用的方法确实是最简单的方法。我很想能够在字符串内引用变量,但在JavaScript中,你需要使用连接(或其他查找/替换方法)。像你和我这样的人可能对PHP有点太依赖了。 - SikoSoft
4
使用Underscore.js template - Jahan Zinedine
3
连接不等同于插值,用户询问如何执行插值。我认为Lev最终提供了答案,即在JS原生中没有办法做到这一点。我不明白为什么这不是一个真正的问题。 - gitb
5
如果我被允许回答,现在有一种解决方案可供新的解释器使用(2015年的FF和Chrome已经支持):https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/template_strings。例如,``Show GRAPH of: ${fundCode}-${funcCode}`` 将给我 Show GRAPH of : AIP001-_sma (在字符串1周围使用反引号,无法在此处显示)。 - Marcos
显示剩余6条评论
22个回答

925
自ES6开始,您可以使用模板文字(Template literals):template literals

const age = 3
console.log(`I'm ${age} years old!`)

P.S. 请注意使用反引号:``


16
我在想为什么他们要使用反引号,"${age}"不足以进行插值吗? - Laughing Lemonade
27
向后兼容性。如果以那种方式实现,所有打印该格式字符串的现有代码都将失败。 - thefourtheye
2
@Jonathan 我一直不喜欢反引号作为有意义的字符。但我不确定这个原因是普遍的还是只适用于我的语言键盘布局。反引号是一种特殊的字符,等待下一个字符被写入时才被写入,需要敲两次才能写入(此时会写入两个)。这是因为一些字符与其互动,如“e”。如果我尝试用它们写“ello”,我会得到èllo``。它的位置也有点烦人(shift+forwardtick),需要按shift键才能输入,不像“'”。 - felix
3
@felix 这是与你的键盘布局相关的事情;你描述的被称为"dead keys"。它们存在于挪威键盘布局中(我来自那里),这也是我在编程时切换到美国键盘布局的原因之一。(还有许多其他字符,例如[和]在美国键盘上更容易输入。) - Eivind Eklund
2
@felix 专业提示:我使用的是挪威键盘,它具有您所描述的完全功能。在按下反引号后,您可以按空格键,以便简单地“使其出现”,而无需将其放置在字符上方或强制使用两个反引号。 :) - Adrian Wiik

264

简述

如果适用的话,使用ECMAScript 2015的模板字符串字面量。

解释

根据ECMAScript 5规范,没有直接的方法来实现,但是在ECMAScript 6中有模板字符串,在规范草案起草期间也被称为准文字面量。像这样使用它们:

> var n = 42;
undefined
> `foo${n}bar`
'foo42bar'

您可以在 {} 内使用任何有效的 JavaScript 表达式。例如:

> `foo${{name: 'Google'}.name}bar`
'fooGooglebar'
> `foo${1 + 3}bar`
'foo4bar'

另一个重要的事情是,您不再需要担心多行字符串。您可以将它们简单地编写为:
> `foo
...     bar`
'foo\n    bar'

注意: 我使用io.js v2.4.0来评估上述所有模板字符串。您也可以使用最新的Chrome测试上述示例。

注意: ES6规范现已最终确定, 但尚未被所有主流浏览器实现。
根据Mozilla开发者网络页面所述,基本支持将从以下版本开始实施: Firefox 34,Chrome 41,Internet Explorer 12。如果您是Opera,Safari或Internet Explorer用户,并且现在对此感到好奇,此测试平台可用于玩耍,直到每个人都获得对此的支持。


4
如果你使用babeljs,那么它是可用的,因此你可以在你的代码库中引入它,一旦你需要支持的浏览器实现了它,你就可以放弃转译步骤。 - ivarni
有没有方法将标准字符串转换为模板字符串字面量?例如,如果有一个包含翻译表的 JSON 文件需要将值插入其中以进行显示,该怎么办?我认为第一种解决方案在这种情况下可能很有效,但我总体上喜欢新的字符串模板语法。 - nbering
2
为了观众中视力较差的人,`字符与'不同。如果您无法使其正常工作,请确保使用重音符号(又称倾斜引号、反引号)https://en.wikipedia.org/wiki/Grave_accent。它在键盘的左上角 :) - Quincy
@RB,我想你是指左上角? - stuckj
@stuckj 不是的 - 在英国的 Mac 键盘上肯定是在左下角:https://www.apple.com/uk/shop/product/MLA22B/A/magic-keyboard-british-english。我现在意识到美国的键盘是在左上角 - 不知道为什么英国的键盘如此奇怪(因为在英国键盘上放置它是不寻常的地方)。疯狂的苹果。 - RB.
显示剩余3条评论

213

Douglas Crockford的《JavaScript救急手册》中包含了一个String.prototype.supplant函数。该函数简短易用,使用方法非常熟悉:

String.prototype.supplant = function (o) {
    return this.replace(/{([^{}]*)}/g,
        function (a, b) {
            var r = o[b];
            return typeof r === 'string' || typeof r === 'number' ? r : a;
        }
    );
};

// Usage:
alert("I'm {age} years old!".supplant({ age: 29 }));
alert("The {a} says {n}, {n}, {n}!".supplant({ a: 'cow', n: 'moo' }));

如果你不想改变String的原型,你可以将其适配为独立的,或者放置在其他命名空间中,或者做其他的处理。


109
注意:这样做比简单地拼接要慢10倍。 - cllpse
37
并且需要输入三倍左右的按键和字节。 - ma11hew28
8
你能否提供一些关于速度降低的观点?比如,从0.01秒降至0.1秒(重要)还是从0.000000001秒降至0.00000001秒(不重要)? - chris
16
在我的电脑上做了一个快速测试,使用Crockford的方法处理"The cow says moo, moo, moo!"这句话用时7312纳秒,而使用预编译函数将变量从传递的对象中提取出来并将它们与模板字符串的常量部分连接起来,则只需111纳秒。这个测试在Chrome 21上进行。 - George
14
或者像这样使用:" {0} 说 {1},{1},{1}!".supplant(['cow', 'moo']) - Robert Massa
显示剩余7条评论

56

小心警告:避免使用任何不允许您转义其自身分隔符的模板系统。例如,使用此处提到的supplant()方法将无法输出以下内容。

"由于我的{age}变量,我今年3岁了。"

简单插值可能适用于小型独立脚本,但通常会带有这种设计缺陷,限制任何严肃使用。我更喜欢DOM模板,如:

<div> I am <span id="age"></span> years old!</div>

使用jQuery操作:$('#age').text(3)

或者,如果您只是厌倦了字符串拼接,还有另一种语法:

var age = 3;
var str = ["I'm only", age, "years old"].join(" ");

4
附注:Array.join() 的速度比直接使用(+ 方式)连接要慢,因为浏览器引擎(包括 V8,其中包括 Node.js 和几乎所有运行 JS 的内容)已经对其进行了巨大的优化,并且明显更偏向于直接连接 - pilau
1
supplant方法确实允许您生成所提到的字符串:只有在数据对象包含名为“token”的成员时,{token}才会被替换 - 因此只要您不提供具有“age”成员的数据对象,就可以正常工作。 - Chris Fewtrell
1
克里斯,我不认为那是一个解决方案。我可以轻松地更新示例以同时使用年龄变量和 {age} 字符串。你真的想根据模板复制文本来命名变量吗?--另外,自从这篇文章发表之后,我成为了数据绑定库的忠实粉丝。像 RactiveJS 这样的库将使你的 DOM 不再充斥着变量 span。与 Mustache 不同的是,它只会更新页面的那一部分。 - Grae Kindel
3
你的主要回答似乎假定这个JavaScript正在浏览器环境中运行,尽管该问题没有标记为HTML。 - canon
3
关于 supplant 的您的“谨慎提示”是没有必要的:"I am 3 years old thanks to my {age} variable.".supplant({})) 将返回给定的字符串。如果给定了 age,可以使用 {{age}} 来打印 {}。请让我知道您是否需要任何其他帮助。 - le_m
显示剩余5条评论

37
我在很多语言中都使用这种模式,当我还不知道如何正确地做,只是想快速地把一个想法记录下来。
// JavaScript
let stringValue = 'Hello, my name is {name}. You {action} my {relation}.'
    .replace(/{name}/g    ,'Inigo Montoya')
    .replace(/{action}/g  ,'killed')
    .replace(/{relation}/g,'father')
    ;

虽然不是特别高效,但我觉得它很易读。它总是有效的,而且随时可用。
' VBScript
dim template = "Hello, my name is {name}. You {action} my {relation}."
dim stringvalue = template
stringValue = replace(stringvalue, "{name}"    ,"Luke Skywalker")     
stringValue = replace(stringvalue, "{relation}","Father")     
stringValue = replace(stringvalue, "{action}"  ,"are")

始终如一

* COBOL
INSPECT stringvalue REPLACING FIRST '{name}'     BY 'Grendel Mother'
INSPECT stringvalue REPLACING FIRST '{relation}' BY 'Son shoulder'
INSPECT stringvalue REPLACING FIRST '{action}'   BY 'made a gaping mortal-making wound upon.'

更新:
我本以为这是不言而喻的,但有人向我明确表示并非如此。只要你有一个键/值对需要遍历,你都可以随时应用这个方法。
// JavaScript
let template = 'Hello, my name is {name}. You {action} my {relation}.'
let values = {
  name: 'Inigo Montoya',
  action: 'killed',
  relation, 'father',
};
let output = template;
for(let entry of Object.entries(values)){
  output = output.replace('{'+output[0]+'}',output[1]);
}
console.log(output);

我最近一直在使用这个在PL/SQL中。

2
IE不支持模板字面量,因此当您不想要求使用单独的软件包(如sprintf)时,这是一个不错的解决方案。比字符串连接更清晰,特别是在处理具有动态属性和内容的HTML标记时。 - CJ Edgerton
1
IE 应该消失了。 - refex

25

如果你真的想要用大锤砸开一个坚果,你可以使用Prototype的模板系统

var template = new Template("I'm #{age} years old!");
alert(template.evaluate({age: 21}));

使用原型,您可以直接调用插值函数 interpolate http://api.prototypejs.org/language/String/prototype/interpolate/(内部使用了模板)。 - rvazquezglez

23

试试sprintf库(一个完整的开源JavaScript sprintf实现)。例如:

vsprintf('The first 4 letters of the english alphabet are: %s, %s, %s and %s', ['a', 'b', 'c', 'd']);

vsprintf接受一个参数数组,并返回一个格式化的字符串。


16

最简单的方法是

`my string ${VARIABLE}`

一个效率较低的方式是:

function format(str, ...params) {
  for(const param of params)
    str = str.replace("%", param);
   return str;
}

可以与...一起使用

format("My % string", "interpolation")

13
如果你想在 console.log 输出中进行插值,那么只需要:
console.log("Eruption 1: %s", eruption1);
                         ^^

在这里,%s 被称为“格式说明符”。console.log 内置了这种插值支持。

这是当前问题的最简单答案,除非有其他更好的答案。 - Facundo Colombier

12

使用ES6 模板字符串 并使用任何可用的转换器(如babel)将其转译为ES5。

const age = 3;

console.log(`I'm ${age} years old!`);

http://www.es6fiddle.net/im3c3euc/


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接