October 22, 2018











In this article, we cover ways to join strings. Even though it includes old methods of concatenating strings, we focus on template literals. Here you can learn how they work and how to expand their functionality with tags. Let’s go!

Before template literals

Back in the days, we didn’t have template literals and to merge strings we used other techniques. Some of them were:

Addition operator

As mentioned in the Looking into assembly code of coercion, if any operands of the + operator is a string, the result will also be a string. Such operation converts the other operand to a string if needed.

1 2 3 const dogName = 'Fluffy' ; console . log ( 'My dog, ' + dogName + ', is a great pet!' ) ; // My dog, Fluffy, is a great pet!

Since the string is a primitive type, it is immutable. It means that it can’t be altered and when attempting to add one string to the other, a new one is created. This applies to all of the ways of concatenating strings.

1 2 let greetings = 'Hello' ; greetings += ', have a great day!' ; // a brand new string created here

Trying to modify an existing string will not have any effect and throw an error in the strict mode!

1 greetings [ 0 ] = 'h' ;

Uncaught TypeError: Cannot assign to read only property ‘0’ of string ‘Hello’

Object descriptors ensure that – all characters in the string array are read-only.

1 Object . getOwnPropertyDescriptor ( greetings , '0' ) . writable // false

Check out Object property descriptors, if you would like to know more about them

String.prototype.concat

The concat function joins together the calling string with provided arguments and returns a new string. An important thing is that it does not change the string, but returns a new one.

1 2 3 const hello = 'Hello' ; console . log ( hello . concat ( ' World!' ) ) ; // Hello World! console . log ( hello ) ; // Hello

According to the MDN, using the addition operator is much faster.

Template literals

The template literals, previously also called template strings, are a new way to join strings introduced in ES6. Such strings are enclosed by the back-tick – `` – the grave accent. Template literals can evaluate expressions inserted as a part of the string, enclosed in a dollar sign and curly brackets.

1 2 3 const dogName = 'Fluffy' ; console . log ( ` My dog , $ { dogName } , is a great pet ! ` ) ; // My dog, Fluffy, is a great pet!

It looks clean and straightforward, especially combined with the usage of good variable names.

If you ever need to put a back-tick into the string, you need to escape it: to do this, put a backslash before the backtick.

1 console . log ( ` Back - tick : ` ` ) ; // Back-tick: `

A noteworthy aspect of using template literals are multi-line strings. If you press enter in the middle of a template literal, you insert a newline character.

1 2 3 4 5 6 const pets = ` Dog : Fluffy Cat : Garfield ` ; console . log ( pets ) ; // Dog: Fluffy // Cat: Garfield

To achieve the same output without template strings, you need to insert newline characters manually:

1 2 3 4 5 const pets = 'Dog: Fluffy

Cat: Garfield' ; console . log ( pets ) ; // Dog: Fluffy // Cat: Garfield

Hitting enter in a regular string causes Uncaught SyntaxError: Invalid or unexpected token and to prevent it, you need to escape newlines.

1 2 const pets = 'Dog: Fluffy\ Cat: Garfield' ;

This does not insert newline characters though and you still need to add it manually.

Tagged templates

When you use regular template literals, your input is passed to a default function that concatenates it into a single string. An interesting thing is that you can change it by preceding the template literal with your function name that acts as a tag. By doing it, you create a tagged template.

Using tags is a more advanced way to put the template literals to work: with them, you can manipulate your strings before outputting them.

The first argument of the tag function is an array of string values from your literal. The rest of the arguments are the expressions. The return of the tag function is the output of the tagged template literal.

1 2 3 4 5 6 7 8 9 10 const dog = { name : 'Fluffy' } function petTag ( strings , pet ) { console . log ( strings ) ; // ['My dog, ', ', is a great pet!']; return ` $ { strings [ 0 ] } $ { pet . name } $ { strings [ 1 ] } ` ; } console . log ( petTag ` My dog , $ { dog } , is a great pet ! ` ) ; // My dog, Fluffy, is a great pet!

Imagine wanting to bold every expression that is a part of your template literal when using markdown. To bold something in markdown, you need to enclose it in asterisks.

1 2 3 4 5 6 7 8 9 10 11 function bold ( strings , . . . expressions ) { return strings . reduce ( ( result , currentString , i ) = > ( ` $ { result } $ { currentString } $ { expressions [ i ] ? ` * $ { expressions [ i ] } * ` : '' } ` ) , '' ) } const dogName = 'Fluffy' ; const age = 3 ; console . log ( bold ` The name of my dog is $ { dogName } . He is $ { age } years old . ` ) ; // The name of my dog is *Fluffy*. He is *3* years old

A common use-case is in a popular library styled-components. It is a good example that tag functions don’t need to return a string:

1 2 3 4 5 6 7 import styled from 'styled-components' ; const Element = styled . div ` background : url ( $ { props = > props . imageUrl } ) ` console . log ( typeof Element ) ; // object

In this case the styled.div returns an object and your tag functions can also do that!

Summary

In this article, we covered string concatenation. It included going through the old ways of joining strings like the addition operator and functions like String.prototype.concat. We covered template strings, a new way of concatenating strings that came with the ES6. We learned about how they work and how to enhance their functionalities with tags.