TECHY360
Everything You Need To Know About Tech

What and how in ES6: tricks, best practices, and examples. Part one. let/const, blocks, arrow functions, strings, destructuring, modules, parameters, classes

0 34

A cheat sheet for everyday use, containing a selection of ES2015 [ES6] tips with examples. Share your tips in the comments! 

var vs let / const

In addition to  var us, 2 new identifiers are now available for storing values ​​- let and  const. In contrast  varletthey  const have a block scope.

Usage example  var:

var snack = 'Meow Mix';

function getFood(food) {
    if (food) {
        var snack = 'Friskies';
        return snack;
    }
    return snack;
}

getFood(false); // undefined

But what happens when replacing  var with  let:

let snack = 'Meow Mix';

function getFood(food) {
    if (food) {
        let snack = 'Friskies';
        return snack;
    }
    return snack;
}

getFood(false); // 'Meow Mix'

Such a change in behavior indicates that you need to be careful when refactoring the old code in which it is used  var. A simple change  var to  let can lead to unpredictable program behavior.

Note : let and are  const visible only in their block. Thus, an attempt to call them before the declaration will result in  ReferenceError.

console.log(x); // ReferenceError: x is not defined

let x = 'hi';

Best Practice : Leave  var in legacy code for further thorough refactoring. When working with new code, use  let for variables whose values ​​will change, and  const – for immutable variables.

Replacing Immediately Called Functions (IIFE) with Blocks

Usually,  immediately called functions  are used to enclose values ​​in their scope. In ES6, you can create block scopes.

(function () {
    var food = 'Meow Mix';
}());

console.log(food); // Reference Error

ES6 Blocks Example:

{
    let food = 'Meow Mix';
};

console.log(food); // Reference Error

Arrow functions

When using nested functions, it is often necessary to separate the context  this from its lexical scope. An example is given below:

function Person(name) {
    this.name = name;
}

Person.prototype.prefixName = function (arr) {
    return arr.map(function (character) {
        return this.name + character; // Cannot read property 'name' of undefined
    });
};

A common solution to this problem is to store the context  this in a variable:

function Person(name) {
    this.name = name;
}

Person.prototype.prefixName = function (arr) {
    var that = this; // Store the context of this
    return arr.map(function (character) {
        return that.name + character;
    });
};

We can also pass the desired context  this:

function Person(name) {
    this.name = name;
}

Person.prototype.prefixName = function (arr) {
    return arr.map(function (character) {
        return this.name + character;
    }, this);
};

Or bind context:

function Person(name) {
    this.name = name;
}

Person.prototype.prefixName = function (arr) {
    return arr.map(function (character) {
        return this.name + character;
    }.bind(this));
};

Using  arrow functions, the lexical meaning is  this not hidden, and the above code can be rewritten as follows:

function Person(name) {
    this.name = name;
}

Person.prototype.prefixName = function (arr) {
    return arr.map(character => this.name + character);
};

Best practice : Use  arrow functions  whenever you need to preserve lexical meaning  this.

Arrow functions are also more understandable when used to write functions that simply return a value:

var squares = arr.map(function (x) { return x * x }); // Function Expression
const arr = [1, 2, 3, 4, 5];
const squares = arr.map(x => x * x); // Arrow Function for terser implementation

Best practice : use  arrow functions  instead of function expressions when appropriate.

Strings

With the advent of ES6, the standard library has grown dramatically. In addition to previous changes, string methods such as  .includes() and appeared  .repeat().

.includes ()

var string = 'food';
var substring = 'foo';

console.log(string.indexOf(substring) > -1);

Instead of checking the return value  > -1 to check for the substring, you can use  .includes()that returns a boolean:

const string = 'food';
const substring = 'foo';

console.log(string.includes(substring)); // true

.repeat ()

function repeat(string, count) {
    var strings = [];
    while(strings.length < count) {
        strings.push(string);
    }
    return strings.join('');
}

In ES6, everything is much simpler:

// String.repeat(numberOfRepetitions)
'meow'.repeat(3); // 'meowmeowmeow'

Template Literals

Using template literals, we can safely use special characters in strings.

var text = "This string contains \"double quotes\" which are escaped.";
let text = `This string contains "double quotes" which don't need to be escaped anymore.`;

Template literals  also support interpolation, which makes the task of concatenating strings and values:

var name = 'Tiger';
var age = 13;

console.log('My cat is named ' + name + ' and is ' + age + ' years old.');

Much simpler:

const name = 'Tiger';
const age = 13;

console.log(`My cat is named ${name} and is ${age} years old.`);

In ES5, we handled line breaks like this:

var text = (
    'cat\n' +
    'dog\n' +
    'nickelodeon'
);

Or so:

var text = [
    'cat',
    'dog',
    'nickelodeon'
].join('\n');

Template literals  save line breaks:

let text = ( `cat
dog
nickelodeon`
);

Template literals can also process expressions:

let today = new Date();
let text = `The time and date is ${today.toLocaleString()}`;

Restructuring

Destructuring allows us to extract values ​​from arrays and objects (even nested ones) and put them into variables in a more convenient way.

Array destructuring

var arr = [1, 2, 3, 4];
var a = arr[0];
var b = arr[1];
var c = arr[2];
var d = arr[3];
let [a, b, c, d] = [1, 2, 3, 4];

console.log(a); // 1
console.log(b); // 2

Object Restructuring

var luke = { occupation: 'jedi', father: 'anakin' };
var occupation = luke.occupation; // 'jedi'
var father = luke.father; // 'anakin'
let luke = { occupation: 'jedi', father: 'anakin' };
let {occupation, father} = luke;

console.log(occupation); // 'jedi'
console.log(father); // 'anakin'

Modules

Before ES6 we had to use a library like  Browserify to create modules on the client-side, and require in  Node.js. With ES6, we can directly use modules of any type (AMD and CommonJS).

Export to CommonJS

Related Posts
1 of 26
module.exports = 1;
module.exports = { foo: 'bar' };
module.exports = ['foo', 'bar'];
module.exports = function bar () {};

Export to ES6

In ES6, we can use different types of exports.

Named Export :

export let name = 'David';
export let age  = 25;

Export  object list :

function sumTwo(a, b) {
    return a + b;
}

function sumThree(a, b, c) {
    return a + b + c;
}

export { sumTwo, sumThree };

We can also export functions, objects, and values ​​simply by using the keyword  export:

export function sumTwo(a, b) {
    return a + b;
}

export function sumThree(a, b, c) {
    return a + b + c;
}

Finally, you can  export the default bindings :

function sumTwo(a, b) {
    return a + b;
}

function sumThree(a, b, c) {
    return a + b + c;
}

let api = {
    sumTwo,
    sumThree
};

export default api;

/* Which is the same as
 * export { api as default };
 */

Best practice : always use the method  export default at the end of the  module. This will clearly show what is exported and save time.

Import to ES6

ES6 provides various types of imports. We can import the whole file:

import 'underscore';

It is important to note that  importing the entire file will lead to the execution of all the code at the external level of this file .

Like Python, there is a named import:

import { sumTwo, sumThree } from 'math/addition';

Which can be renamed:

import {
    sumTwo as addTwoNumbers,
    sumThree as sumThreeNumbers
} from 'math/addition';

In addition, you can  import the namespace :

import * as util from 'math/addition';

Finally, a list of values ​​from the module:

import * as additionUtil from 'math/addition';
const { sumTwo, sumThree } = additionUtil;

The import from the binding looks like this by default:

import api from 'math/addition';
// Same as: import { default as api } from 'math/addition';

Exporting is best simplified, but sometimes you can mix default imports with something else. When we export like this:

// foos.js
export { foo as default, foo1, foo2 };

You can import them like this:

import foo, { foo1, foo2 } from 'foos';

When importing a module exported using CommonJS syntax (as in React), you can do:

import React from 'react';
const { Component, PropTypes } = React;

This can be simplified:

import React, { Component, PropTypes } from 'react';

Note : exported values ​​are bindings , not links. Therefore, a change in one module will entail a change in another.

Parameters

In ES5, there were several ways to handle functions with default values, undefined arguments, and named parameters. In ES6, all this is implemented, and with clear syntax.

Default options

function addTwoNumbers(x, y) {
    x = x || 0;
    y = y || 0;
    return x + y;
}

In ES6, you can simply specify the default function parameters:

function addTwoNumbers(x=0, y=0) {
    return x + y;
}

addTwoNumbers(2, 4); // 6
addTwoNumbers(2); // 2
addTwoNumbers(); // 0

Residual parameters

In ES5, an undetermined number of arguments was processed like this:

function logArguments() {
    for (var i=0; i < arguments.length; i++) {
        console.log(arguments[i]);
    }
}

Using the  residual operator, you can pass an indefinite number of arguments:

function logArguments(...args) {
    for (let arg of args) {
        console.log(arg);
    }
}

Named Parameters

One of the ES5 templates for working with named parameters was the options object template, taken from jQuery.

function initializeCanvas(options) {
    var height = options.height || 600;
    var width  = options.width  || 400;
    var lineStroke = options.lineStroke || 'black';
}

The same can be obtained using the destructor as a formal parameter of the function:

function initializeCanvas(
    { height=600, width=400, lineStroke='black'}) {
        // Use variables height, width, lineStroke here
    }

If we want to make all the values ​​optional, we can destruct the empty object:

function initializeCanvas(
    { height=600, width=400, lineStroke='black'} = {}) {
        // ...
    }

Extension operator

In ES5, you could find the maximum array using the method  apply above  Math.max:

Math.max.apply(null, [-1, 100, 9001, -32]); // 9001

In ES6, you can use the extension operator to pass an array of values ​​as function parameters:

Math.max(...[-1, 100, 9001, -32]); // 9001

You can also intuitively concatenate literal arrays:

let cities = ['San Francisco', 'Los Angeles'];
let places = ['Miami', ...cities, 'Chicago']; // ['Miami', 'San Francisco', 'Los Angeles', 'Chicago']

Classes

Prior to ES6, classes needed to be created by adding properties to the constructor function, expanding the prototype:

function Person(name, age, gender) {
    this.name   = name;
    this.age    = age;
    this.gender = gender;
}

Person.prototype.incrementAge = function () {
    return this.age += 1;
};

And the extended classes are like this:

function Personal(name, age, gender, occupation, hobby) {
    Person.call(this, name, age, gender);
    this.occupation = occupation;
    this.hobby = hobby;
}

Personal.prototype = Object.create(Person.prototype);
Personal.prototype.constructor = Personal;
Personal.prototype.incrementAge = function () {
    Person.prototype.incrementAge.call(this);
    this.age += 20;
    console.log(this.age);
};

ES6 provides very convenient syntactic sugar. Classes can be created this way:

class Person {
    constructor(name, age, gender) {
        this.name   = name;
        this.age    = age;
        this.gender = gender;
    }

    incrementAge() {
      this.age += 1;
    }
}

And expand – using the keyword  extends:

class Personal extends Person {
    constructor(name, age, gender, occupation, hobby) {
        super(name, age, gender);
        this.occupation = occupation;
        this.hobby = hobby;
    }

    incrementAge() {
        super.incrementAge();
        this.age += 20;
        console.log(this.age);
    }
}

Best practice : although this syntax hides the implementation, it will be easier for beginners to understand it, and the written code will be cleaner.

Comments
Loading...

This website uses cookies to improve your experience. We'll assume you're ok with this, but you can opt-out if you wish. Accept Read More