774

Consider this code:

var age = 3;

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

Are there any other ways to insert the value of a variable in to a string, apart from string concatenation?

thefourtheye
  • 233,700
  • 52
  • 457
  • 497
Tom Lehman
  • 85,973
  • 71
  • 200
  • 272
  • This question needs clarification. Are you looking for more information on string concatenation? – Corey Sunwold Sep 10 '09 at 23:42
  • 7
    You could checkout CoffeeScript: http://coffeescript.org/ – dennismonsewicz Jul 17 '12 at 13:40
  • 2
    As others have indicated, the method you are using *is* the easiest way to go about it. I would love to be able to refer to variables inside strings, but in javascript you need to use concatenation (or some other find/replace approach). Folks like you and I are probably just a little too attached to PHP for our own good. – SikoSoft Mar 15 '13 at 22:35
  • 4
    Use Underscore.js [template](http://underscorejs.org/#template) – Jahan Zinedine Oct 04 '13 at 12:52
  • 3
    Concatenation is not interpolation and the user asked how to do interpolation. I think Lev finally provided the answer, i.e. there is no way to do this in native JS. I do not understand why this not a real question. – gitb Oct 22 '14 at 16:28
  • var x = 'x', y = 'y'; console.log('x: %s, y: %s', x, y) – Cody Dec 17 '14 at 20:20
  • HERE! var x = 'x', y = 'y'; console.log('x: %s, y: %s', x, y) – Cody Dec 17 '14 at 20:21
  • 5
    If I could be allowed to answer, there is now a solution starting for newer interpretters (2015 FF and Chrome already support): https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/template_strings . Eg. ``Show GRAPH of : ${fundCode}-${funcCode}`` gets me `Show GRAPH of : AIP001-_sma` (use backticks around string 1....cannot seem to display here) – Marcos Feb 05 '15 at 15:30
  • Another sledgehammer: [jquery-tmpl](https://github.com/nje/jquery-tmpl) (templating with jQuery). – Jo Liss Feb 12 '11 at 02:54
  • Does this answer your question? [How to interpolate variables in strings in JavaScript, without concatenation?](https://stackoverflow.com/questions/3304014/how-to-interpolate-variables-in-strings-in-javascript-without-concatenation) – Ivar May 01 '21 at 13:41
  • Sometime template strings are not enough. [Here's an interpolator](https://stackblitz.com/edit/web-platform-nmqf7o?file=script.js) that may be handy. – KooiInc Aug 12 '22 at 08:27

22 Answers22

900

Since ES6, you can use template literals:

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

P.S. Note the use of backticks: ``.

Damjan Pavlica
  • 31,277
  • 10
  • 71
  • 76
  • 15
    I'm wondering why they went with backticks on this, wouldn't "${age}" be enough to do interpolation? – Laughing Lemonade Dec 19 '18 at 08:22
  • 26
    @LaughingLemonade Backward compatibility. If it was implemented that way, all the existing code which prints a string in that format will fail. – thefourtheye Dec 21 '18 at 07:02
  • 2
    @Jonathan I always dislike backticks as a meaningful character. But I am unsure if the reason is universal or only applies to my languages keyboard-layouts. Backtick is a special sort of character that waits with being written until the next character, requiring it to be hit twice to write (and writes two of them at this time). This is because some characters interact with them, such as "e". If I try to write "ello" with them, I get èllo``. It is also somewhat annoyingly located (shift+forwardtick, which is another such special lookahead character), as it requires shift to type, unlike '. – felix Jun 14 '19 at 08:20
  • 3
    @felix it's a think that's specific to your keyboard layout; what you're describing is called "dead keys". They're present in the Norwegian keyboard layout (where I am from) as well, which is part of the reason I switch to a US keyboard layout for all programming. (There's a number of other characters - e.g. [ and ] - that are much easier on a US keyboard as well.) – Eivind Eklund Mar 06 '20 at 11:49
  • 2
    @felix Protip: I'm on a Norwegian keyboard which has that exact functionality you describe. You can hit space after hitting backtick to simply "make it appear" without putting it above a character or forcing two backticks. :) – Adrian Wiik Nov 09 '22 at 12:51
262

tl;dr

Use ECMAScript 2015's Template String Literals, if applicable.

Explanation

There is no direct way to do it, as per ECMAScript 5 specifications, but ECMAScript 6 has template strings, which were also known as quasi-literals during the drafting of the spec. Use them like this:

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

You can use any valid JavaScript expression inside the {}. For example:

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

The other important thing is, you don't have to worry about multi-line strings anymore. You can write them simply as

> `foo
...     bar`
'foo\n    bar'

Note: I used io.js v2.4.0 to evaluate all the template strings shown above. You can also use the latest Chrome to test the above shown examples.

Note: ES6 Specifications are now finalized, but have yet to be implemented by all major browsers.
According to the Mozilla Developer Network pages, this will be implemented for basic support starting in the following versions: Firefox 34, Chrome 41, Internet Explorer 12. If you're an Opera, Safari, or Internet Explorer user and are curious about this now, this test bed can be used to play around until everyone gets support for this.

thefourtheye
  • 233,700
  • 52
  • 457
  • 497
  • 4
    It's usable if you use [babeljs](https://goo.gl/8HHxG0), so you can introduce it in your codebase and then later drop the transpilation step once the browsers you need to support implements it. – ivarni Oct 29 '15 at 20:00
  • Is there a way to convert a standard string to a template string literal? For example, if one had a json file containing a translation table that needed values interpolated into them for display purposes? I think the first solution probably works well for this situation, but I do like the new string template syntax in general. – nbering Mar 03 '16 at 19:14
  • As this is still one of the first search results on js string interpolation, it would be great if you could update it to reflect the general availability now. "There is no direct way to do it" is probably not true anymore for the majority of browsers. – Felix Dombek Mar 13 '17 at 19:50
  • 2
    for the poor eyesight in the audience, the ` character is different than ' . If you have trouble getting this to work make sure your using the grave quote, (aka tilted quote , back quote) https://en.wikipedia.org/wiki/Grave_accent . It's at the top left on your keyboard :) – Quincy Apr 13 '17 at 10:38
  • @Quincy Bottom left of a Mac keyboard - also known as a back-tick :) – RB. May 04 '17 at 13:49
  • @RB, I think you mean top left? – stuckj Sep 21 '17 at 19:52
  • @stuckj Nope - definitely bottom left on a Mac (UK) keyboard: https://www.apple.com/uk/shop/product/MLA22B/A/magic-keyboard-british-english. The US ones are top-left I realise now - wonder why the UK ones are so odd (because it's an unusual place to have it on a UK keyboard as well). Crazy Apple. – RB. Sep 21 '17 at 20:40
  • Ah, didn't realize it was different on a UK keyboard. Learn something new everyday I guess. :) – stuckj Sep 21 '17 at 21:28
210

Douglas Crockford's Remedial JavaScript includes a String.prototype.supplant function. It is short, familiar, and easy to use:

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' }));

If you don't want to change String's prototype, you can always adapt it to be standalone, or place it into some other namespace, or whatever.

Chris Nielsen
  • 14,731
  • 7
  • 48
  • 54
  • 107
    Note: This will run ten times slower than just concatenating. – cllpse Sep 11 '09 at 07:00
  • 36
    And take about three times more keystrokes & bytes. – ma11hew28 Jun 09 '11 at 16:23
  • 8
    @roosteronacid - Can you give some perspective on that speed decrease? Like, from 0.01s to 0.1s (important) or from 0.000000001s to 0.00000001s (irrelevant)? – chris Oct 02 '12 at 16:52
  • 16
    @george: A quick test on my machine gave 7312 ns for "The cow says moo, moo, moo!" using Crockford's method vs 111 ns for a precompiled function that pulls the variables out of the passed object and concatenates them with the constant parts of the template string. This was Chrome 21. – George Oct 25 '12 at 21:13
  • 14
    Or use it like: `"The {0} says {1}, {1}, {1}!".supplant(['cow', 'moo'])` – Robert Massa Feb 19 '13 at 13:31
  • 1
    Ordinary interpolation on syntax level like in PHP or Ruby will just parse interpolation into usual concatenation when parsing code, I suppose. On the other hand this solution implies multiple substring search. – Gherman Aug 29 '14 at 11:33
  • So, the "nano second" performance difference is basically irrelevant for all intents and purposes. – Nelson Rodriguez Nov 08 '17 at 17:19
  • As George has proven, the time is irrelevant to human existence. Unless your use case is millions of strings. – SoEzPz Apr 29 '19 at 03:38
  • And is not short or familiar – Benj Jun 11 '19 at 15:48
  • It also modifies an object that you don't own, namely, the `String`'s prototype. Never mutate Objects that you don't own. – Emile Bergeron Nov 06 '19 at 15:59
  • 1
    This was a great starting point. I ended up adding some single-level ternary `if ? then : else` and literal parsing ([gist](https://gist.github.com/micimize/2ce3c82f0af97505360601d510fe76e0)) If you're interpolating in-code, use template strings, but this is a great starting point for exposing a simple template-driven api to end users – micimize Dec 11 '19 at 23:08
  • this is really useful when sending placeholders around. – juan Isaza Apr 10 '21 at 17:10
56

Word of caution: avoid any template system which does't allow you to escape its own delimiters. For example, There would be no way to output the following using the supplant() method mentioned here.

"I am 3 years old thanks to my {age} variable."

Simple interpolation may work for small self-contained scripts, but often comes with this design flaw that will limit any serious use. I honestly prefer DOM templates, such as:

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

And use jQuery manipulation: $('#age').text(3)

Alternately, if you are simply just tired of string concatenation, there's always alternate syntax:

var age = 3;
var str = ["I'm only", age, "years old"].join(" ");
Grae Kindel
  • 2,664
  • 1
  • 24
  • 18
  • 4
    Side note: `Array.join()` is slower than direct (`+` style) concatenation, because browser engines (which includes V8, which includes node and almost anything that runs JS today) have optimized it massively and there's a great deal of difference in favor of [direct concatenation](http://jsperf.com/string-concatenation/47) – pilau Nov 19 '13 at 15:17
  • 1
    The supplant method does allow you to generate the string you mention: the {token} is only replaced if the data object contains a member called `token` - thus provided that you do not provide a data object that has an `age` member, it will be fine. – Chris Fewtrell Feb 01 '14 at 15:18
  • 1
    Chris, I don't see how that's a solution. I could easily update the example to use both an age variable and the {age} string. Do you really want to be worrying about what you can name your variables based on the template copy text? --Also, since this post I've become a big fan of data binding libraries. Some, like RactiveJS, will save you from a DOM laden down with variable spans. And unlike Mustache, it only updates that part the page. – Grae Kindel Oct 23 '14 at 15:13
  • `'I am {age} years old thanks to my {v} variable.'.supplant({age:3, v:'{age}'})` – Wizek Mar 20 '15 at 22:33
  • 1
    Yeah, but then your data model needs to know about all the copy text that might needs escaping/replacing, and your template author would need to have access to the JS. Would be easier to add a negative lookahead to the supplant regex to allow escaping. – Grae Kindel Mar 25 '15 at 18:40
  • 3
    Your primary answer seems to assume that this javascript is running in a browser environment even though the question isn't tagged with HTML. – canon Oct 29 '15 at 19:46
  • 3
    Your "word of caution" regarding `supplant` is unwarranted: `"I am 3 years old thanks to my {age} variable.".supplant({}));` returns exactly the given string. If `age` were given, one could still print `{` and `}` using `{{age}}` – le_m Jul 13 '16 at 20:52
  • fair point, as an accident of `data["{age}"]` not being defined. would have been nice to have it documented, but it _still_ runs into the problem of the template developer needing to know if some crazy developer defined that property as it's not real escaping. – Grae Kindel Oct 14 '16 at 16:07
  • @greg.kindel escaping requires the exact same level of knowledge. You're making a fairly big deal of an edge case. If you were right in it not being possible it would be significant, but this is very minor imo. – Vala Nov 16 '16 at 10:30
  • @le_m That will work unless you're working within an AngularJS environment where the Angular interpreter would then analyze the age variable and you're back to having it expose the value again. – CSS Jan 11 '19 at 17:00
36

I use this pattern in a lot of languages when I don't know how to do it properly yet and just want to get an idea down quickly:

// 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')
    ;

While not particularily efficient, I find it readable. It always works, and its always available:

' 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")

ALWAYS

* 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.'

UPDATE:

I had thought it was self-evident, but it was made clear to me that it was not. You can apply this anytime you have a key/value pair to loop through.

// 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){
  output = output.replace('{'+output[0]+'}',output[1]);
}
console.log(output);

I've been using this in pl/SQL lately.

Jefferey Cave
  • 2,507
  • 1
  • 27
  • 46
  • 2
    IE does not support template literals, so this is a nice solution when you do not want to require a separate package such as sprintf. Much cleaner than concatenating strings, especially when working html tags that have dynamic attributes and content. – CJ Edgerton Jun 22 '20 at 18:04
  • IE should just die – refex Mar 10 '23 at 17:58
25

You could use Prototype's template system if you really feel like using a sledgehammer to crack a nut:

var template = new Template("I'm #{age} years old!");
alert(template.evaluate({age: 21}));
rzymek
  • 9,064
  • 2
  • 45
  • 59
shuckster
  • 5,279
  • 2
  • 23
  • 22
  • Using prototype you can call interpolate directly http://api.prototypejs.org/language/String/prototype/interpolate/ (internally it uses Template) – rvazquezglez Dec 02 '15 at 02:03
23

Try sprintf library (a complete open source JavaScript sprintf implementation). For example:

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

vsprintf takes an array of arguments and returns a formatted string.

Paolo
  • 20,112
  • 21
  • 72
  • 113
NawaMan
  • 901
  • 5
  • 12
16

The simplest would be

`my string ${VARIABLE}`

a less efficient way to accomplish this would be

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

which can be used with

format("My % string", "interpolation")
Cory Lewis
  • 559
  • 5
  • 6
13

If you want to interpolate in console.log output, then just

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

Here, %s is what is called a "format specifier". console.log has this sort of interpolation support built-in.

12

You can do easily using ES6 template string and transpile to ES5 using any available transpilar like babel.

const age = 3;

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

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

Mohammad Arif
  • 6,987
  • 4
  • 40
  • 42
9
let age = 3;

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

You can use the backticks `` and ES6 template string

Force Bolt
  • 1,117
  • 9
  • 9
6

Try kiwi, a light-weight JavaScript module for string interpolation.

You can do

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

or

Kiwi.compose("I'm %{age} years old!", {"age" : age});
zs2020
  • 53,766
  • 29
  • 154
  • 219
6

Here's a solution which requires you to provide an object with the values. If you don't provide an object as parameter, it will default to using global variables. But better stick to using the parameter, it's much cleaner.

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>
Lucas Trzesniewski
  • 50,214
  • 11
  • 107
  • 158
  • 3
    Don't use `eval`, `eval` is evil! – chris97ong Jul 20 '14 at 13:17
  • 5
    @chris97ong While that's "true", at least give a reason ("evil" doesn't help) or alternate solution. There's almost always a way around using `eval`, but sometimes not. For example, if the OP wanted a way to interpolate using the current scope, without having to pass a lookup object (like how Groovy interpolation works), I'm pretty sure `eval` would be required. Don't just resort to the old "eval is evil". – Ian Jul 20 '14 at 13:41
  • 1
    never use eval nor ever suggest it to be used – hasen Jan 04 '15 at 12:04
  • 3
    @hasenj this is why I said it *"may not be the best idea"* and provided an alternative method. But unfortunately, **`eval` is the only way to access local variables in scope**. So don't reject it just because it hurts your feelings. BTW, I also prefer the alternative way because it's safer, but the `eval` method is what *precisely* answers OP's question, hence it's in the answer. – Lucas Trzesniewski Jan 04 '15 at 13:14
  • 1
    The problem with `eval` is that is cannot access vars from another scope, so if your `.interpolate` call is within another function, and not global, it's not going to work. – georg Jul 26 '15 at 10:36
  • @georg you're right, I only tested the global scope when writing this snippet, and it somehow didn't occur to me it couldn't work with other scopes unless you inline the `eval` call. This makes the `eval` solution useless, as the other one can handle global scope just fine. I removed it entirely. Thanks for pointing this out. – Lucas Trzesniewski Jul 26 '15 at 11:51
  • [Don't modify objects you don't own.](https://stackoverflow.com/q/14034180/1218980) – Emile Bergeron Nov 06 '19 at 16:08
4

Couldn't find what I was looking for, then found it -

If you're using Node.js, there's a built-in utilpackage with a format function that works like this:

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

Coincidentally this is now built into console.log flavors too in Node.js -

console.log("This really bad error happened: %s", "ReferenceError");
> This really bad error happened: ReferenceError
B Fish
  • 323
  • 2
  • 7
2

Expanding on Greg Kindel's second answer, you can write a function to eliminate some of the boilerplate:

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

Usage:

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!');
Community
  • 1
  • 1
James Ko
  • 32,215
  • 30
  • 128
  • 239
2

I can show you with an example:

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'));
Sivakumar Tadisetti
  • 4,865
  • 7
  • 34
  • 56
shades3002
  • 879
  • 9
  • 11
1

Since ES6, if you want to do string interpolation in object keys, you will get a SyntaxError: expected property name, got '${' if you do something like:

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

You should do the following instead:

let obj = { [`${age}`]: 3 }
Ethan Chen
  • 649
  • 1
  • 6
  • 17
1

Supplant more for ES6 version of @Chris Nielsen's post.

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."
SoEzPz
  • 14,958
  • 8
  • 61
  • 64
0

Using template syntax fails in older browsers, important if you are creating HTML for public use. Using concatenation is tedious and hard to read, particularly if you have many or long expressions, or if you must use parentheses to handle mixtures of number and string items (both of which use the + operator).

PHP expands quoted strings containing variables and even some expressions using a very compact notation: $a="the color is $color";

In JavaScript, an efficient function can be written to support this: var a=S('the color is ',color);, using a variable number of arguments. While there is no advantage over concatenation in this example, when the expressions get longer this syntax may be clearer. Or one can use the dollar sign to signal the start of an expression using a JavaScript function, as in PHP.

On the other hand, writing an efficient workaround function to provide template-like expansion of strings for older browsers wouldn't be hard. Someone has probably done it already.

Finally, I imagine that sprintf (as in C, C++, and PHP) could be written in JavaScript, although it would be a little less efficient than these other solutions.

David Spector
  • 1,520
  • 15
  • 21
0

Custom flexible interpolation:

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>
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
vsync
  • 118,978
  • 58
  • 307
  • 400
0

While templates are probably best for the case you describe, if you have or want your data and/or arguments in iterable/array form, you can use String.raw.

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

With the data as an array, one can use the spread operator:

const args = [3, 'yesterday'];
String.raw({
  raw: ["I'm ", " years old as of ", ""]
}, ...args);
Brett Zamir
  • 14,034
  • 6
  • 54
  • 77
0
let age = 3;

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

console.log(`I'm ${age} years old`); //should work fine

const btn = `<input type='buttong' onclick='myFunction(${age})'>`;// in this case it will look for a variable named 3

const btn = `<input type='buttong' onclick='myFunction("${age}")'>`;// in this case value of age will be passed to your function
Sumit Kumar
  • 1,855
  • 19
  • 19