13

I'm writing a es6 tag function for template literals, which first checks a condition in the string and, if the condition isn't found, merely interprets the template literal as if it were untagged. I am curious if, from my tag function, there is a way to call the browser's native template literal function (which I assume would be faster than my own implemented function). Bonue: With this, couldn't there be an opportunity for tag composition, eg htmlEscape(unindentfoobar);

eg.

function dumbTag(strs, ...vals) {
    vals = vals.map((val,i) =>
            (i % 2 == 0 ? 'even:' : 'odd:')+val);
    return String.template(strs, ...vals);
}

my own implemented function - is there a faster way / way to call what the browser does?

function template(strs, ...vals) {
    let result = strs[0];
    for (let [i,val] of vals.entries()) {
        result += val;
        result += strs[i+1];
    }
    return result;
}
Aaron_H
  • 1,623
  • 1
  • 12
  • 26

4 Answers4

12

You can (ab)use String.raw (the only built-in tag) for this purpose:

function doNothingTag() {
  arguments[0] = { raw: arguments[0] };
  return String.raw(...arguments);
}

// Or in a more modern style:
const doNothingTag = (strings, ...rest) => String.raw({ raw: strings }, ...rest);

doNothingTag`It ${true ? 'works!' : 'fails'}`
// "It works!"

doNothingTag`Even\nwith\nescape\nsequences!`
// "Even
// with
// escape
// sequences!"

This is essentially just tricking String.raw into thinking that the escape-interpreted string is the raw version.

Steve Bennett
  • 114,604
  • 39
  • 168
  • 219
Inkling
  • 3,544
  • 4
  • 30
  • 44
  • So, as long as you know for certain that you don't have any escape sequences in your templates, you can use `String.raw` "as is", i.e. `const tag = String.raw;`. – Ron Inbar Jun 13 '21 at 15:11
2

There is no such builtin function - untagged template literals are just evaluated straight to strings.

is there a faster way?

That depends a lot on the implementation. In case you are using a transpiler, I would avoid using rest parameters, iterators and for of loops:

function template(strs) {
    var result = strs[0];
    for (var i=1; i < strs.length; i++) {
        result += arguments[i];
        result += strs[i];
    }
    return result;
}
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
1

I was also wondering if there is such a native function. In the meantime, this is what I use:

const tag = ([head, ...tail], ...args) => tail.reduce((a, b, i) => a + args[i] + b, head);
Ron Inbar
  • 2,044
  • 1
  • 16
  • 26
  • `const tag = (strings, ...values) => values.reduce( (s, v, i, strings) => \`${s}${v}${strings[i + 1]}\`, strings[0] ); // A slightly shorter version` – Jean-Rene Bouvier Jun 12 '21 at 17:33
0

A short implementation could be done by using Array.prototype.flatMap() like this:

const defaultTag = (strs, ...vals) =>
  strs.flatMap((x, i) => [x, i < vals.length ? vals[i] : undefined]).join('');

const name = 'Some name';
const age = 32;

console.log(defaultTag`Hi my name is ${name}, and I'm ${age} years old!`);
Guerric P
  • 30,447
  • 6
  • 48
  • 86