13

I came across this code:

new Array(10).fill('1').join``;

I do not know what the meaning of the signs `` used immediately after .join is.

I thought the correct syntax would be new Array(10).fill('1').join('').

Any ideas are welcome thanks!

const data = new Array(10).fill('1').join``;
console.log(data)
Boann
  • 48,794
  • 16
  • 117
  • 146
Radex
  • 7,815
  • 23
  • 54
  • 86
  • 5
    It's a [tagged template literal](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals). It's exactly the same (in this case) as `.join('');`, that trick is frequently used for code golf or DSLs. – Jared Smith Nov 24 '20 at 19:58
  • 1
    Tag functions are great, but it's worth noting a couple of things about that specific code: 1. ```new Array(10).fill('1').join`` ``` is an awfully long-winded, roundabout way to write `"1".repeat(10)`. 2. Even if `repeat` weren't an option, calling `Array.prototype.join` as a tag function in order to get a blank separator like that is at best pointless (outside of code golf; it saves two characters of source code vs `join("")`) and at worst creates unnecessary memory churn and extra work: The engine has to create two arrays (the template array and its `raw` array); ... – T.J. Crowder Nov 25 '20 at 07:41
  • 1
    ... then it passes the outer one to `join`, which has call `toString` on it in order to use it as a separator. Does it matter in any given case? Almost certainly not. But it's just a bit silly to use ```.join`` ``` instead of `.join("")`. – T.J. Crowder Nov 25 '20 at 07:43

4 Answers4

10

What you're seeing is a tagged template literal, part of the ES6 specification. That pattern is heavily used in e.g. Google's Polymer Project (now lit-element). It invokes the supplied function (or in this case method) with the supplied template literal.

In addition to being used to create a Domain-Specific Language (DSL) of sorts, it's also frequently used to reduce the byte count for Javascript answers in code golf In the case of the Google project I linked, it's used as part of a domain-specific HTML templating language.

customcommander
  • 17,580
  • 5
  • 58
  • 84
Jared Smith
  • 19,721
  • 5
  • 45
  • 83
  • 1
    With a DSL, you can make a slimmed down version of a programming language that can be used for limited interactivity, For examlpe, you can expose a configuration where you can write "money < 0" and then interpret that rule at runtime, so you can have few operators and few variables with some syntax to allow more complex rules to be configured. So, the overall complexity of your code isn't big but you allow *more* things to be done with the application. – VLAZ Nov 24 '20 at 20:16
  • 1
    Just FWIW, there's a canonical for this: https://stackoverflow.com/questions/29660381/backticks-calling-a-function – T.J. Crowder Nov 25 '20 at 07:44
  • 1
    @T.J.Crowder thanks, I did a couple google searches worth of due diligence and didn't turn up a dupe, was kind of surprised it hadn't already been asked. Although of course in retrospect the search string that would have led me to it is fairly obvious. We need a better way of marking questions as canonicals for finding them! – Jared Smith Nov 25 '20 at 12:14
  • 1
    Amen to that brother. That's one of (but just one of) the reasons I don't downvote good answers to duplicate questions. It's a failure of the mechanism. And if the SE system values pointing new questions at existing resources, basic game theory says it should reward that behavior. It doesn't. :-) – T.J. Crowder Nov 25 '20 at 13:31
8

arr.join``; is a tagged template literal.

It applies join (or any other function) to "the content of the template literal string".

When I say "applies to the content of the template literal string" it is actually a little bit more complicated than that: all static parts of the string are put into an array as the first argument and the interpolated values as the remaining arguments.

A simple example will make it easier to understand:

var demo = (str, ...names) => {
  console.log(str);
  console.log(names);
};

var users = ['john', 'jane', 'joe'];

demo`Members: ${users[0]}, ${users[1]} and ${users[2]}.`;
// LOG: ["Members: ", ", ", " and ", "."]
// LOG: ["john", "jane", "joe"]

demo``;
// LOG: [""]
// LOG: []

The last line answers your question because arr.join``; is the same as arr.join(""); just two characters shorter which is useful when you compete in JS1K for example.

In this particular case I'm not sure that the author wanted to save a few bytes because we can shorten the initial statement as follow:

new Array(10).fill('1').join``;
new Array(10).fill(1).join``;
Array(10).fill(1).join``;
'1'.repeat(10);
'1111111111';

Is a tagged template just a glorified function call?

These two expressions may look similar but they are not:

var yy = 20;
year`19${yy}`;
year(`19${yy}`);

In the first expression we have access to the static and dynamic parts of the string: "19" and 20. In the second expression we only get the "final" string "1920".

This is useful when one wants to preserve intent yet allow complex text processing behind the scene.

We know that this is unsafe:

var sql = `SELECT * FROM users where email="${email}" AND password="${password}"`;

To mitigate the risk of SQL injection, it is not uncommon to see things like:

var sql = select('*').from('users').where('AND').eq('email', email).eq('password', password);

However it could be argued that we have traded the expressiveness of SQL with a somewhat awkward API that people are less likely to be familiar with.

If a tagged template literal has access to both static and dynamic parts of a string we can build domain-specific tagged template literals:

var sql = safe_sql`SELECT * FROM users where email="${email}" AND password="${password}"`;
customcommander
  • 17,580
  • 5
  • 58
  • 84
1

This syntax is called Tagged Template literals , Calling a function by passing backitlists.

It works that way

function taggedTemplate(str, ...exps){
   //str is an [] containg the strings in the tagged literals.
   //exps is an [] containg the expressions passes in the tagged literals
   console.log(str[0]) // "Number "
   console.log(exps[0]) // "19"
 }
taggedTemplate`Number ${19}`

So, The join method supports tagged literals, It could be something like this.

Array.prototype.join = function(arg){
  let str = typeof arg === "string" ? arg : arg[0];
  //joins the array using str
}
Sherif Wael
  • 435
  • 2
  • 9
  • 3
    Actually, `.join` doesn't explicitly *support* this technique, just coerces its first argument to a string, that calls `.toString()` on the passed array, that calls its `.join` and transforms it to the string passed in, and *then* joins the main array with it. Check this by running `[1,2,3].join\`a${'b'}c\``. It returns `'1a,c2a,c3'`, as `.join` receives `['a','c']` as first argument and `['a','c'].toString()` is `'a,c'` – FZs Nov 24 '20 at 20:42
-2

It is an abomination that works. So you are correct in assuming that when you do a join function call, you pass a empty string so that there is nothing put in between the resulting string.

In JavaScript, you can declare strings with "quotes", 'apostrophes', and `template literals`.

The developer who wrote that line of code thought they were being clever by skipping the opening and closing parentheses, when in reality they just made the code slower, more complex, and more confusing for everyone else.

FZs
  • 16,581
  • 13
  • 41
  • 50
John
  • 5,942
  • 3
  • 42
  • 79
  • This answer is [being discussed on Meta](https://meta.stackoverflow.com/questions/403122/is-this-answer-insulting-the-developer-who-wrote-a-piece-of-code-really-ok). If you have a concern about its contents or suitability, please post an answer on the Meta question, rather than flagging this for moderator attention. – Cody Gray - on strike Nov 24 '20 at 23:12
  • Template literals are not string literals, and only produce strings when they're untagged. The one in the OP's question is a tagged template literal, which produces a template array (which is passed to the tag function), not a string (though the array will always have at least one string in it). It's up to the tag function what it returns. – T.J. Crowder Nov 25 '20 at 13:37
  • @T.J.Crowder The join function is being passed an array that contains 1 item which is an empty string. Since the only argument that join accepts is a string (not an array), it would do a default join on the array that was passed, concatenating all the items together. Then uses that as the separator. The code is incorrectly using the tag template feature for no apparent reason. – John Nov 25 '20 at 22:33
  • @T.J.Crowder a correction to the comment above is that it joins all the items in an array with a comma as a separator. Not an empty string. Then it uses that comma separated array string as a separator. – John Nov 25 '20 at 22:40
  • 2
    @John - No, it ends up using an empty string. See [my comment here](https://stackoverflow.com/questions/64993915/what-does-join-mean-in-javascript/64993999?noredirect=1#comment114916352_64993915) and [the specification](https://tc39.es/ecma262/#sec-array.prototype.join). (A quick test of ```[1, 2, 3].join``;``` might have been worthwhile, too. :-) ) – T.J. Crowder Nov 26 '20 at 07:17
  • @T.J.Crowder You can view the answer by creating a custom function and using the tag template feature. If you pass an empty tag template, it does result in an empty string because when join merges the array that is passed into it and it has no items. The resulting string is empty. That does not mean that join is not being passed an empty array and that join is not doing join(",") on that passed array internally. – John Nov 28 '20 at 21:06
  • @John - Yes, I know (and frankly, if you read the comment I linked to, it's fairly obvious that I know that) I was responding to your comment above saying *"...it joins all the items in an array with a comma as a separator. Not an empty string."* – T.J. Crowder Nov 29 '20 at 12:42
  • I suppose you misunderstand what I'm saying. If you do [1, 2, 3].join([4, 5, 6]). Then the separator will be the string "4,5,6" so the resulting string will be "14,5,624,5,63". Because join`` passes an empty array aka [] the separator has nothing to put the commas inbetween and the resulting separator used will be an empty string aka "". The issue is that there is no need to use a tag template and it is inefficient to make join concat an empty array using toString() into an empty string instead of just passing an empty string. – John Nov 30 '20 at 02:13