JavaScript: Select ES6 and Later Features

let, const, var

These three keywords are used to declare named properties – variables or constants. What is the difference then, and why do we need three of those?

Properties declared with let and const are block scoped, while var is global or function scoped, depending on where it’s declared.

Let’s analyze this code in the browser console:

{
    var nameVar = 'John';
    let nameLet = 'Jack';
    const nameConst = 'Jon';
}

console.log('var:', nameVar);
console.log('let:', nameLet);
console.log('const:', nameConst);

This becomes important when we create programs with nested functions, methods and loops where it’s important to prevent leaking of scope-defined member out of their scope. E.g.:

for (let i = 0; i < 10; i++){
    console.log(i);
}

In the above code snippet, the variable i is only available within the scope of the loop itself, and cannot be accessed from outside that block.

The difference between let and const is that if a property is declared with const, its value becomes immutable. Try this:

let firstName = 'Svilen';
const lastName = 'Stoicheff';

firstName = 'Steve';
lastName = 'Stevenson';

Upon assigning a different value to lastName you will get a JS type error.

Further reading: Let, Const, Var

Arrow Functions

Arrow functions provide a convenient compact way of creating anonymous functions or function expressions. A note to remember is that they are not a complete replacement for the conventional function syntax, and certain features of traditional functions are unavailable to arrow functions. See further reading for a detailed explanation.

Traditional anonymous function converted to arrow function:

function (a) {
   return a + ' is great!';
}

(a) => {return a + ' is great!'}

a => a + ' is great!';

The above syntax significantly shortens the code and simplifies the program where anonymous functions are used as callbacks, e.g.:

document.querySelectorAll('.step').forEach(el => el.classList.remove('hidden'));

Instead of:

document.querySelectorAll('.step').forEach(function(el) {
   el.classList.remove('hidden');
});

Arrow functions as function expressions:

var callGreat = function(a) {
   return a + ' is great!';
}

var callGreat = a => a + ' is great!';

Further Reading: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions

Template Literals

Template literals – strings declared with backticks (“) – solved two long-standing JS annoyances in code-rendered text: Multiple lines and expression interpolation (variable values inside the text).

For many years we used to concatenate text like this:

function myBands () {
   var like = 'Deep Purple';
   var hate = 'The Rolling Stones';
   console.log( 'I like ' + like +'\nbut I hate ' + hate + '!');
}

myBands();

As of ES6, we can write the above like this:

function myBands () {
   var like = 'Deep Purple';
   var hate = 'The Rolling Stones';
   console.log( `I like ${like}
But I hate ${hate}!`);
}
myBands();

The above syntax is a lot more convenient, uses the familiar convention ${…} for expression interpolation, and is less error prone, because it removes the need for endless sub-string concatenation in the following manner: ‘ ‘+ + ‘ ‘+ +’ ‘

I’ve been trying to practice what I preach – here are a couple of examples where I made ample use of the above features:
https://unifiedbrands.net/wp-content/themes/DH-Blank-Theme/js/prep_table_selector.js
https://unifiedbrands.net/wp-content/themes/DH-Blank-Theme/js/cheeser_roi_calculator.js

String Methods

ES6 added the methods includes(), startsWith() and endsWith() to the String constructor. These add more convenience and speed to parsing and searching strings, compared to the ubiquitous indexOf().

const poem = 'If music be the food of love, play on!'
poem.startsWith('If'); //true
poem.includes('music'); //true
poem.endsWith('play on!'); //true

Spread syntax (…)

The spread syntax allows for an array, string or object to be expanded in place. See the next several uses – note that a string cannot just be expanded into another string.

let arr1 = ['a','b','c','d'];
let str1 = 'abcd';
let obj1 = {a:1, b:2, c:3, d:4};

let arr2 = [...arr1, 'e'];
let str2Arr = [...str1, 'e']; //create an array from string
let obj2 = {...obj1, e:5};

The above code illustrates one practical use of the spread syntax – to create copies of existing arrays and objects and modify them without affecting the original arrays or objects. There are other ways of copying objects, the spread operator just adds convenience.

Why is creating new copies in the above manner important? Can’t we just use assignment, like this: let obj2 = obj1? Let’s analyze this code:

let arr = ['a','b','c','d'];
let obj = {a:1, b:2, c:3, d:4};

let arr2 = arr; 
let obj2 = obj; 

arr2.push('e');
obj2.e = 5;

Output arr and obj to the console and you will see that they also got changed, when we added a new array member and a new object property to arr2 and obj2. That’s because in JavaScript, copies of objects (arrays are also objects) by assignment are actually pointers to the same memory address. So in the above code arr and arr2 are just pointers to the same array, and obj and obj2 – to the same object. So modifying any of those also modifies the original object. Therefore we need to use different techniques to create copies by creating new objects – and the ES6 spread operator is arguably the most convenient way to do so.

Further reading: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax

JavaScript Modules

With JavaScript modules, we can export any object or value as a named module at the time of its declaration, and then import it in a top level module and use it. Here is a simple example:

function shakespeareQuote (quote){
console.log(`William Shakespeare once said:
   ${quote}`);  
}
export {shakespeareQuote};
import {shakespeareQuote} from './js/modules.js';

shakespeareQuote('To be or not to be - that is the question.');

One more thing: In order for the main module to be recognized as such, it needs to be added to the page with the type of ‘module’ in the script tag, like this:

<script type="module" src="module.js"></script>

Historically, we’ve been through different module architectures, such as AMD modules with require.js, and CommonJS modules. JS modules have also been known as ES6 modules, or ECMAScript modules as well.

Nowadays the pattern is standardized and built into the language, so we don’t have to worry about third party tools any more.

Further reading:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export