JavaScript Template Literals

Photo credit by Hongkiat

Traditionally, strings in JavaScript were limited, especially if you were working in languages like Ruby and Python. In both of those languages string interpolation meant that you could substitute strings and have multiline string concatenation without wonky hacks.

In JavaScript, there wasn’t anything like this until ES6. Now, we have JavaScript Template Literals or template strings.

Syntax

Template strings use back-ticks rather than quotes to denote a string. A template string could look like this:

let hello = `Hello World!`;

String Substitutions

What’s nice about string substitutions is that you can take any JavaScript expression and place it inside a Template Literal and it will be output as part of the string.

The syntax for this:

let name = “Tiffany”;
console.log(`Hey, ${name}!`);

Since string substitutions in Template Literals are JavaScript expressions we can evaluate any type of expression we want in the Template Literal. We can evaluate a mathematical expression such as:

let a = 13;
let b = 20;
console.log(`The Mac first launched ${a+b} years ago. I, for one, welcome my bitten fruit overlord.`);

//  => The Mac launched 33 years ago. I, for one, welcome my bitten fruit overlord.

console.log(`I am old, but not ${2 * (a+b)} yet.`);

//  => I am old, but not 66 yet.
// I know. It doesn’t make sense. Bear with my pitiful examples.

You can even put functions or method calls inside a Template Literal:


// Functions inside expressions function func() { return “I am the result of foo” } console.log(`bar ${func} baz.`); // => I am the result of foo bar baz. //Method calls let hacker = {name: ‘Elliot Anderson’} console.log(`You forgot to quit Vim, ${hacker.name.toUpperCase()}`); // => You forgot to quit Vim, ELLIOT ALDERSON

Multiline Strings

Multiline strings in JavaScript required hacks like the backslash hack and string concatenation like:

let greeting = “Hello “ +
“World”;

Template strings make this a whole lot easier. Add new lines where you need them and the whitespace inside the backticks inside the Template Literal will be included inside the string like so:

console.log(`I'm a string on one line
I’m a string on another line`);

Tagged Templates

Tagged Templates can be used for powerful string transformations. You create a Tagged Template by placing a function name before the template string. Here’s an example of auto escaping an HTML function such that:

html`<p title="${title}">Hello ${name}!</p>`

returns a string with the appropriate variables substituted but with all the unsafe characters replaced.

Nicholas Zakas goes over this in detail in his book Understanding ES6.

Summary

Template Literals or Template Strings are a powerful addition to the JavaScript language that brings it much needed string interpolation and transformation.

Use Cases for the Spread Operator

One of the coolest features of ES6 is the Spread Operator. The syntax looks a bit like:

let a = [4, 5, 6];
let b = [7, …a, 8];

console.log(b);

This will give us:

View post on imgur.com

The spread operator replaces the concat function in ES5:

“use strict“;
var a = [4, 5, 6];
var b = [7].concat(a, [8]);

console.log(b);

This is a great little feature, but what can we do with it?

Some Uses for the Spread Operator

We can do a lot with the spread operator.

Combine Arrays

You can use the spread operator to combine arrays:

let test = () => {
  let arr1 = ['foo', 'bar', 'baz'];
  let arr2 = ['fizz', 'buzz'];
  arr1.push(...arr2); // Will append the contents of arr2 to arr1
};

test(); // Runs function

Copying Arrays

We’ve used Array.prototype.slice to make copies of arrays. Now we can do that with the spread operator.

let arr = [7, 8, 9];
let arr2 = […arr];
arr2.push(10); // Will return a deep copy of the array with 10 appended to the end
//of the values

More At the MDN

You can find more uses for the spread operator at the MDN

Understanding Hoisting in JavaScript

\"\"

 

With all this talk about ES6 and block scoping with let and const, I thought it would be a good idea to talk about hoisting in JavaScript.

This won’t be a thorough exposition. You can find a great deal of information on GitHub. What I want to do is give the basics.

The JavaScript Interpreter

There are parts to the JavaScript compiler you’d need to understand to get a grip on what happens when a variable is hoisted or lifted to the top of a scope.

There are three parts to the compiler:

  • The Engine
  • Compiler
  • Scope

Looking at this statement: var b = 10, you’re probably assuming this is one statement, and that the JavaScript interpreter sees it the same way. It doesn’t.

The interpreter takes this statement and breaks it up into what are called tokens. To the interpreter, this statement and its tokens are var, b, =, and 10.

The engine part of the interpreter will compile these tokens into an array and subsequent tree of nested elements to execute. The code is compiled right before it is executed.

What is Meant by Hoisting?

I am assuming here that you know what scope is, as I have talked about block scoping before with the same assumption. If you don’t, I’ll cover it in more detail another time. For now, I am just going to focus on hoisting.

Dictionary.com definition of hoist:

verb (used with object)

1.

to raise or lift, especially by some mechanical appliance:

to hoist a flag; to hoist the mainsail.

You can think of hoisting a variable as lifting it to the top of whatever scope it is in.

The difference here is that JavaScript hoists declarations and leaves the initializations at the bottom of the scope.

Example:

[javascript]

console.log(foo);

var foo = 3;

[/javascript]

Take this statement:

var foo = 3;

JavaScript looks at this and sees:

var foo; and foo = 3;

The first part of the statement is compiled, the second part is executed. It is then compiled as:

[javascript]

var foo;

console.log(foo);

foo = 3;

[/javascript]

As you can see, the declaration in this scope is the only thing that was “raised” to the top of the scope. The initialization remains at the bottom.

Just the Basics

I encourage you to check out the link I linked to above. Kyle Simpson is an amazing teacher and I’ve learned so much from the YDKJS series. You can find the free version on GitHub.

Function vs. Block Level Scoping with let and const

This is an exposè on the difference between function and block level scope using let and const in JavaScript. The following explanation is how I’ve come to understand them so far 1.

Function or Lexical Scope

Take this code:

[javascript]

function foo(a) {

var b = 2;

// some code

function bar() {

// …

}

// more code

var c = 3;

}

[/javascript]

Here we have a function foo that sits in the global scope. Nested within this scope is the function bar() and nested inside foo. Also within foo, we have the variable declaration var b = 2; and var c = 3;. Each of these belong to scope— foo to the global scope, var b, var b, and var c all scope to foo. Each of these scopes have access to functions and identifiers of the scopes outside them. None of these functions or identifiers are accessible outside of the function scope foo.

Disadvantages of Function Scopes

  • Hoisting by the interpreter

    The first pass the interpreter does on var processes it and lifts it to the top of the scope. It then processes the function declarations as uninitialized variables. 2

    An example of hoisting:

    [javascript]

    function bar() {

    if (!foo) {

    var foo = 10;
    }

    alert(foo);
    }

    bar(); // alerts 10 instead of reference error

    [/javascript]

  • Having to use an IIFE 3

    For beginners like me, this is confusing syntax. I see all IIFE as closures and I am sure that’s not the case. For instance:

    [javascript]

    (function(){

    // all your code here

    var foo = function() {};

    window.onload = foo;

    // …

    })();

    // foo is unreachable here (it’s undefined)

    [/javascript]

    In this example, the parentheses before function turn this block of code into a function expression. The final (); calls the function immediately after it is created. This creates a block scope for foo because it is nested within the function expression and invoked immediately. The syntax is hard to understand and thus remember.

Creating Block Scopes with let and const

You can easily create block scopes with let. It is a new keyword in ES6 that makes it stylistically easier to create a block scope in some block of code (between {...}). The variable cannot be accessed in the global scope. Take this code for example:

[javascript]

if (a) {

b = a * 2;

let a = 5;

}

console.log(b); // Reference Error! a is not defined.

[/javascript]

The error here is because cosole.log() can’t access the a variable as let scopes it to the if block.

let also signals to another developer that the identifier you’re using will be mutated at some point along the way, at least according to Eric Elliott. This is why he suggests using const for immutable identifiers.

const

This creates block scope but allows for the data the identifier holds to be quasi immutable. The data can be mutated. const only is immutable in the sense that it binds the data to the identifier, and thus should be used when only when you want to treat a value as non-reassignable. In this instance, const is not like CONST in Java and other languages.

You can actually make a const value immutable by adding object.freeze() to it.

So let or const?

If you took a minute to read the article I linked to by Eric Elliott, you’ll see his strong opinion in favor of const. He also stated that var is the weakest link in ES6. But Kyle Simpson had something interesting to say about it

let improves scoping options in JS, not replaces. var is still a useful signal for variables that are used throughout the function. Having both, and using both, means scoping intent is clearer to understand and maintain and enforce. That’s a big win!

Thoughts

I am still not sure which is the right one to use and when. I am sticking with let for now, whenever I need block scopes. It is important to note you don’t have to use block scopes all the time. A mixture of function or static scopes and block scopes are fine in the same document.

What do you think? Are you taking advantage of ES6’s let and const?

  1. StackOverflow ftw
  2. An aside: In JavaScript, the interpreter will hoist variable declarations but not initializations.
  3. Immediately Invoked Function Expression