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


533

考虑以下代码:

var age = 3;

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

除了字符串连接之外,还有其他方法可以将变量的值插入到字符串中吗?


7
您可以签出CoffeeScript:coffeescript.org
dennismonsewicz 2012年

1
正如其他人指出的那样,您使用的方法最简单的方法。我希望能够在字符串中引用变量,但是在javascript中,您需要使用串联(或其他一些查找/替换方法)。为了我们自己的利益,像您和我这样的人可能对PHP依附得太少了。
2013年

4
使用Underscore.js 模板
Jahan 2013年

2
串联不是插值,用户询问如何进行插值。我认为Lev终于提供了答案,即在本地JS中没有办法做到这一点。我不明白为什么这不是一个真正的问题。
gitb 2014年

4
如果可以允许我回答,那么现在有一个针对较新的解释器的解决方案(已经支持2015 FF和Chrome):developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… 。例如。Show GRAPH of : ${fundCode}-${funcCode} 让我Show GRAPH of : AIP001-_sma (在字符串1周围使用反引号。...似乎无法在此处显示)
Marcos

Answers:


535

从ES6开始,您可以使用模板文字

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

PS注意反引号的使用:``


3
我想知道为什么他们要反引号,“ $ {age}”不足以进行插值吗?
笑柠檬水

5
@LaughingLemonade向后兼容。如果以这种方式实现,则所有以该格式打印字符串的现有代码都将失败。
thefourtheye

1
@乔纳森·我总是不喜欢反引号作为有意义的角色。但是我不确定原因是普遍的还是仅适用于我的语言的键盘布局。Backtick是一种特殊的字符,它等待写入直到下一个字符,要求将其打两次才能写入(此时写入两个)。这是因为某些字符与其交互,例如“ e”。如果我尝试与他们一起写“ ello”,我会得到èllo``。它的位置也有些令人讨厌(shift + forttick,这是另一个这样的特殊超前字符),因为它需要转换为类型,而不是'。
felix

@felix认为是特定于您的键盘布局的;您所描述的称为“死键”。它们也存在于挪威键盘布局(我来自哪里)中,这是我为所有编程切换到美国键盘布局的部分原因。(还有许多其他字符-例如[和]-在美国键盘上也更容易使用。)
Eivind Eklund

239

tl; dr

如果适用,请使用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 Explorer12。如果您是Opera,Safari或Internet Explorer用户,现在对此感到好奇,这个测试床可以用于游戏,直到每个人都对此表示支持。


3
如果您使用babeljs,则可以使用,因此您可以在代码库中引入它,然后在需要支持的浏览器实现后,立即放弃翻译步骤。
ivarni 2015年

有没有一种方法可以将标准字符串转换为模板字符串文字?例如,是否有一个包含转换表的json文件,该表需要将值插值到其中以用于显示?我认为第一种解决方案可能在这种情况下效果很好,但是我确实喜欢新的字符串模板语法。
nbering

由于这仍然是js字符串插值的首批搜索结果之一,因此,如果您可以对其进行更新以反映现在的一般可用性,那将是很好的。对于大多数浏览器而言,“没有直接的方法可以做到”可能不再适用。
Felix Dombek

2
对于听众视力不佳的人,“性格不同于”。如果您无法使用此工具,请确保使用坟墓引号(又名倾斜引号,反引号)en.wikipedia.org/wiki/Grave_accent 。它在键盘的左上方:)
Quincy

@Quincy Mac键盘的左下角-也称为反勾号:)
RB。

192

Douglas Crockford的Remedial 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的原型,则始终可以将其改编为独立的,或将其放置在其他命名空间中,或其他任何形式。


102
注意:这将比连接慢十倍。
cllpse

36
并花费大约三倍的击键次数和字节数。
ma11hew28 2011年

8
@roosteronacid-您能对速度降低发表一些看法吗?像是从0.01s到0.1s(重要)还是从0.000000001s到0.00000001s(无关)?
克里斯,2012年

16
@george:在我的计算机上进行的快速测试得出了7312 ns的“母牛说says,mo ,,!” 使用Crockford方法和111 ns的方法进行预编译的函数,该函数将变量从传递的对象中拉出,并将它们与模板字符串的常量部分连接在一起。这是
George

13
或像这样使用它:"The {0} says {1}, {1}, {1}!".supplant(['cow', 'moo'])
罗伯特·马萨

54

警告:避免使用任何模板系统不允许您转义其分隔符。例如,使用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,其中包括V8,该节点包括节点以及几乎所有今天运行JS的东西)都对其进行了大规模优化,并且在直接串联方面
pilau

1
supplant方法确实允许您生成您提到的字符串:仅当数据对象包含一个称为的成员时,才替换{token} token-因此,只要您不提供具有age成员的数据对象,就可以了。
克里斯·菲特雷尔

1
克里斯,我不知道这是一个解决方案。我可以轻松地更新示例,以同时使用年龄变量和{age}字符串。您是否真的要担心您可以根据模板副本文本为变量命名?-而且,自从这篇文章以来,我已经成为数据绑定库的忠实拥护者。像RactiveJS这样的工具,可以使您从充满可变跨度的DOM中解放出来。与Mustache不同,它仅更新页面的那一部分。
greg.kindel 2014年

3
您的主要答案似乎是假设该javascript在浏览器环境中运行的,即使该问题未用HTML标记。
佳能2015年

2
您的“警告”一词supplant是不必要的:"I am 3 years old thanks to my {age} variable.".supplant({}));完全返回给定的字符串。如果age给出了,仍然可以打印{}使用{{age}}
le_m

23

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

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

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



21

当我尚不知道如何正确执行此模式时,我想在许多语言中使用此模式,而只是想快速了解一下:

// JavaScript
var stringValue = 'Hello, my name is {name}. You {action} my {relation}.'
    .replace(/{name}/g    ,'Indigo 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'
INSPECT stringvalue REPLACING FIRST '{relation}' BY 'Mother'
INSPECT stringvalue REPLACING FIRST '{action}'   BY 'did unspeakable things to'


6

尝试kiwi,这是一个用于字符串插值的轻量级JavaScript模块。

你可以做

Kiwi.compose("I'm % years old!", [age]);

要么

Kiwi.compose("I'm %{age} years old!", {"age" : age});

6

如果要对console.log输出进行插值,则只需

console.log("Eruption 1: %s", eruption1);
                         ^^

在这里,%s就是所谓的“格式说明符”。console.log具有内置的这种插值支持。


1
这是唯一正确的答案。问题是关于插值而不是模板字符串。
sospedra

5

这是一个解决方案,要求您为对象提供值。如果不提供对象作为参数,则默认使用全局变量。但是最好还是坚持使用参数,因为它更干净。

String.prototype.interpolate = function(props) {
    return this.replace(/\{(\w+)\}/g, function(match, expr) {
        return (props || window)[expr];
    });
};

// Test:

// Using the parameter (advised approach)
document.getElementById("resultA").innerText = "Eruption 1: {eruption1}".interpolate({ eruption1: 112 });

// Using the global scope
var eruption2 = 116;
document.getElementById("resultB").innerText = "Eruption 2: {eruption2}".interpolate();
<div id="resultA"></div><div id="resultB"></div>


3
不使用evaleval是邪恶的!
chris97ong 2014年

5
@ chris97ong虽然那是“ true”,但至少要给出原因(“ evil”无济于事)或其他解决方案。几乎总有一种使用的方法eval,但有时没有。例如,如果OP想要一种使用当前范围进行插值的方法,而不必传递查找对象(例如Groovy插值的工作方式),那么我肯定eval会需要。不要只求助于旧的“评估是邪恶的”。
2014年

1
永远不要使用eval,也不要建议使用它
hasen 2015年

2
@hasenj,这就是为什么我说它“可能不是最好的主意”并提供替代方法的原因。但不幸的是,这eval是访问scope中的局部变量的唯一方法。因此,不要仅仅因为它会伤害您的感觉而拒绝它。顺便说一句,我也更喜欢替代方法,因为它更安全,但是该eval方法恰好回答了OP的问题,因此就在答案中。
卢卡斯·特热涅夫斯基

1
问题eval在于无法从另一个作用域访问var,因此,如果您的.interpolate调用位于另一个函数内,而不是全局函数内,则它将无法正常工作。
乔治,2015年

2

扩展Greg Kindel的第二个答案,您可以编写一个函数来消除一些样板:

var fmt = {
    join: function() {
        return Array.prototype.slice.call(arguments).join(' ');
    },
    log: function() {
        console.log(this.join(...arguments));
    }
}

用法:

var age = 7;
var years = 5;
var sentence = fmt.join('I am now', age, 'years old!');
fmt.log('In', years, 'years I will be', age + years, 'years old!');

1

我可以举一个例子给你看:

function fullName(first, last) {
  let fullName = first + " " + last;
  return fullName;
}

function fullNameStringInterpolation(first, last) {
  let fullName = `${first} ${last}`;
  return fullName;
}

console.log('Old School: ' + fullName('Carlos', 'Gutierrez'));

console.log('New School: ' + fullNameStringInterpolation('Carlos', 'Gutierrez'));


1

从ES6开始,如果您想在对象键中进行字符串插值,SyntaxError: expected property name, got '${'则执行以下操作会得到一个:

let age = 3
let obj = { `${age}`: 3 }

您应该改为执行以下操作:

let obj = { [`${age}`]: 3 }

1

为@Chris Nielsen的帖子的ES6版本替换更多内容。

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

string = "How now ${color} cow? {${greeting}}, ${greeting}, moo says the ${color} cow.";

string.supplant({color: "brown", greeting: "moo"});
=> "How now brown cow? {moo}, moo, moo says the brown cow."

0

在较旧的浏览器中,无法使用模板语法,如果要创建供公众使用的HTML,则很重要。使用串联是一个繁琐且难以阅读的过程,特别是如果您有很多或很长的表达式,或者您必须使用括号来处理数字和字符串项的混合(两者都使用+运算符)时,尤其如此。

PHP使用非常紧凑的表示法扩展了包含变量甚至某些表达式的带引号的字符串: $a="the color is $color";

在JavaScript中,可以编写有效的函数来支持此功能:var a=S('the color is ',color);使用可变数量的参数。尽管在此示例中没有比串联的优势,但是当表达式变长时,此语法可能会更清晰。或者,可以像PHP中一样,使用JavaScript函数使用美元符号来表示表达式的开始。

另一方面,编写有效的解决方法功能以为较旧的浏览器提供类似模板的字符串扩展并不难。可能有人已经做到了。

最后,我想像sprintf(在C,C ++和PHP中)可以用JavaScript编写,尽管它的效率比其他解决方案要低一些。


0

自定义灵活插值:

var sourceElm = document.querySelector('input')

// interpolation callback
const onInterpolate = s => `<mark>${s}</mark>`

// listen to "input" event
sourceElm.addEventListener('input', parseInput) 

// parse on window load
parseInput() 

// input element parser
function parseInput(){
  var html = interpolate(sourceElm.value, undefined, onInterpolate)
  sourceElm.nextElementSibling.innerHTML = html;
}

// the actual interpolation 
function interpolate(str, interpolator = ["{{", "}}"], cb){
  // split by "start" pattern
  return str.split(interpolator[0]).map((s1, i) => {
    // first item can be safely ignored
	  if( i == 0 ) return s1;
    // for each splited part, split again by "end" pattern 
    const s2 = s1.split(interpolator[1]);

    // is there's no "closing" match to this part, rebuild it
    if( s1 == s2[0]) return interpolator[0] + s2[0]
    // if this split's result as multiple items' array, it means the first item is between the patterns
    if( s2.length > 1 ){
        s2[0] = s2[0] 
          ? cb(s2[0]) // replace the array item with whatever
          : interpolator.join('') // nothing was between the interpolation pattern
    }

    return s2.join('') // merge splited array (part2)
  }).join('') // merge everything 
}
input{ 
  padding:5px; 
  width: 100%; 
  box-sizing: border-box;
  margin-bottom: 20px;
}

*{
  font: 14px Arial;
  padding:5px;
}
<input value="Everything between {{}} is {{processed}}" />
<div></div>


0

虽然模板可能最适合您描述的情况,但是如果您具有或想要以可迭代/数组形式的数据和/或参数,则可以使用String.raw

String.raw({
  raw: ["I'm ", " years old!"]
}, 3);

将数据作为数组,可以使用散布运算符:

const args = [3, 'yesterday'];
String.raw({
  raw: ["I'm ", " years old as of ", ""]
}, ...args);

0

找不到我想要的东西,然后找到了-

如果您使用的是Node.js,则有一个内置的util软件包,其格式函数如下所示:

util.format("Hello my name is %s", "Brent");
> Hello my name is Brent

巧合的是console.log,Node.js 现在也将其内置于风味中-

console.log("This really bad error happened: %s", "ReferenceError");
> This really bad error happened: ReferenceError
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.