For basic interpolation a full-blown templating engine seems a bit overkill. Even the “Embedded JS” variants (like Underscore’s & LoDash’s) are not ideal when all you need is simple value interpolation, i.e.

"some/long/url/{userId}/{actionName}?mode={mode}"

The first thing to understand is that runtime performance is key. Doing a match for {...} at runtime is wasteful. We can do it just once, at the beginning and generate a simpler runtime process. This is how most template engines now work, sacrificing creation-time performance for run-time performance.

Resig’s micro-templating approach and other EJS interpolators tend to generate a Function that builds an expression of many concatenations. LoDash, for example, returns the following generated function from _.template('Oh <%=wow%>'):

// Generated Function:
function (obj) {
  obj || (obj = {});
  var __t, __p='', __e=_.escape;
  with (obj) {
    __p += 'Oh ' + ((__t=(wow)) == null ? '' : __t);
  }
  return __p;
}

The wow is treated as a JS expression which is evaluated at runtime within a with statement — which means it can directly access properties of the passed object. It works very well and, its flexibility considered, performance is very good.

For the specialised case of just interpolating known property names we can optimise further. One can ask: What is the fastest way to write the function doInterpolation so that these cases are fulfilled:

doInterpolation({
    foo: 123,
    bar: 456
}); // => 'Foo is 123 ... Bar is 456'
 
doInterpolation({
    foo: 999,
    bar: 111
}); // => 'Foo is 999 ... Bar is 111'

And one would struggle to come up with a more performant function than this:

function doInterpolation(obj) {
  return 'Foo is ' + obj.foo + ' ... Bar is ' + obj.bar;
}

So, given that, in a generic interpolator-generator we should endeavour to generate a function employing the same direct concatenation approach, and that’s what this monstrosity does:

GIST: 6008842

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
 * https://gist.github.com/padolsey/6008842
 * Outputs a new function with interpolated object property values.
 * Use like so:
 *   var fn = makeInterpolator('some/url/{param1}/{param2}');
 *   fn({ param1: 123, param2: 456 }); // => 'some/url/123/456'
 */
var makeInterpolator = (function() {
  var rc = {
    'n': '\n', '"': '\"',
    'u2028': '\u2028', 'u2029': '\u2029'
  };
  return function makeInterpolator(str) {
    return new Function(
      'o',
      'return "' + (
        str
        .replace(/["nru2028u2029]/g, function($0) {
          return rc[$0];
        })
        .replace(/{([sS]+?)}/g, '" + o["$1"] + "')
      ) + '";'
    );
  };
}());

The generated function from makeInterpolator('Oh {wow}') would be:

function (o) {
  return "Oh " + o["wow"] + "";
}

Apart from the closing + "" (I’m calling it a remnant of efficiency), that generated function is the most performant technique to interpolate the varying o.wow value into a pre-defined string. It uses square-bracket notation to access the property values instead of the dot notation just in-case someone ends up using a reserved-word (e.g. `instanceof`) or an invalid JS identifier.

The curly-matching regular expression, /{([sS]+?)}/g, can be modified to your heart’s content. You might, for example, prefer the double-curly {{foo}} or the dollar, $foo.

There are additional features that you could add, such as HTML escaping or echoing empty strings in cases of undefined values… but I think adding more complexity would warrant a proper test-suite, and with each added feature you generalise further and eventually end up with something that has no notable benefit over a generic templating engine.

Thanks for reading! Please share your thoughts with me on Twitter. Have a great day!