ES6 brings more features to the JavaScript language. Some new syntax allows you to write code in a more expressive way, some features complete the functional programming toolbox, and some features are questionable.

let and const

There are two ways for declaring a variable (let and const) plus one that has become obsolete (var).

let

let declares and optionally initializes a variable in the current scope. The current scope can be either a module, a function or a block. The value of a variable that is not initialized is undefined .

Scope defines the lifetime and visibility of a variable. Variables are not visible outside the scope in which they are declared.

Consider the next code that emphasizes let block scope:

let x = 1;
{
let x = 2;
}
console.log(x); //1

In contrast, the var declaration had no block scope:

var x = 1;
{
var x = 2;
}
console.log(x); //2

The for loop statement, with the let declaration, creates a new variable local to the block scope, for each iteration. The next loop creates five closures over five different i variables.

Proper tail-calls

A recursive function is tail recursive when the recursive call is the last thing the function does.

The tail recursive functions perform better than non tail recursive functions. The optimized tail recursive call does not create a new stack frame for each function call, but rather uses a single stack frame.

Promise.all() returns a promise that resolves when all input promises have resolved. Promise.race() returns a promise that resolves or rejects when one of the input promises resolves or rejects.

A promise can be in one of the three states: pending, resolved or rejected. The promise will in pending until is either resolved or rejected.

Promises support a chaining system that allows you to pass data through a set of functions. In the next example, the result of getTodos() is passed as input to toJson(), then its result is passed as input to getTopPriority(), and then its result is passed as input to renderTodos() function. When an error is thrown or a promise is rejected the handleError is called.

In the previous example, .then() handles the success scenario and .catch() handles the error scenario. If there is an error at any step, the chain control jumps to the closest rejection handler down the chain.

All methods defined in the Service class will be added to theService.prototype object. Instances of the Service class will have the same prototype (Service.prototype) object. All instances will delegate method calls to the Service.prototype object. Methods are defined once onService.prototype and then inherited by all instances.

Inheritance

“Classes can inherit from other classes”. Below is an example of inheritancewhere the SpecialService class “inherits” from the Service class:

All methods defined in the SpecialService class will be added to the SpecialService.prototype object. All instances will delegate method calls to the SpecialService.prototype object. If the method is not found in SpecialService.prototype, it will be searched in the Service.prototypeobject. If it is still not found, it will be searched in Object.prototype.

Class can become a bad feature

Even if they seem encapsulated, all members of a class are public. You still need to manage problems with this losing context. The public API is mutable.

class can become a bad feature if you neglect the functional side of JavaScript. class may give the impression of a class-based language when JavaScript is both a functional programming language and a prototype-based language.

Encapsulated objects can be created with factory functions. Consider the next example:

Arrow functions

Arrow functions can create anonymous functions on the fly. They can be used to create small callbacks, with a shorter syntax.

Let’s take a collection of to-dos. A to-do has an id , a title , and a completed boolean property. Now, consider the next code that selects only the title from the collection:

const titles = todos.map(todo => todo.title);

or the next example selecting only the todos that are not completed:

const filteredTodos = todos.filter(todo => !todo.completed);

this

Arrow functions don’t have their own this and arguments. As a result, you may see the arrow function used to fix problems with this losing context. I think that the best way to avoid this problem is to not use this at all.

Arrow functions can become a bad feature

Arrow functions can become a bad feature when used to the detriment of named functions. This will create readability and maintainability problems. Look at the next code written only with anonymous arrow functions:

Our mission: to help people learn to code for free. We accomplish this by creating thousands of
videos, articles, and interactive coding lessons - all freely available to the public. We also have
thousands of freeCodeCamp study groups around the world.

Donations to freeCodeCamp go toward our education initiatives, and help pay for servers, services,
and staff.