ES2015 (ES6)

Here a list of the most used functionalities of ES2015 used in MapStore.

Variable declaration

Other than classical var, you can declare variables using const and let

// cannot be re-assigned new content, static check
const a = “MY_CONST”;

// block scoped var (var was function scope)
let a = “myvar”;

Functions

Default Value

Now is possible to define a default value for function arguments.

function fun(x = 42) { return x };
fun() // returns 42;
fun(6*9) // returns 54

Rest Parameter

The rest parameter syntax allows a function to accept an indefinite number of arguments as an array, providing a way to represent variadic functions in JavaScript.

// rest parameter (array of last parameters)
fun = function (a, ...b) { return b; }
fun(1, 2, 3, 4); // return [2,3,4]

Arrow Function

Arrow function is a concise syntax for writing (anonymous) function expressions.

They are anonymous and can be associated to a variable or passed as arguments to functions:

const my = () => {}; // this is an empty function

if function body is only one line long you can omit brackets {} and it will implicit return the expression value:

() => 1; // this is a function that returns 1;

if the function takes only one argument only, parenthesis for argument ((argument)) list are optionals.

argument => value

zero arguments, more than one argument using when using default values, you need parenthesis.

() => 1;
(arg1 = 1) => arg1; // returns arg1;
(arg1, arg2)  => arg1 + arg2; // returns arg1 + arg2;

Note

For one line arrow function that returns an object, use ({}) (to avoid confusion with {} of function body)

() => ({ object: “to return” })

Note

Unlike regular functions, arrow functions do not have their own this. The value of this inside an arrow function remains the same throughout the lifecycle of the function and is always bound to the value of this in the closest non-arrow parent function.

let name ={
    fullName:'abc',
    printInRegular: function(){
        console.log(`My Name is ${this.fullName}`);
    },
    printInArrow:()=>console.log(`My Name is ${this.fullName}`)
}
name.printInRegular();   // My Name is abc
name.printInArrow();     // My Name is undefined

New Array Methods

Map

The method map create a new array with the same length, with result of a function passed

[1, 2, 3].map(a => a + 1) // [2,3,4]

Filter

The method filter creates a new array with only the elements that pass the test function passed

[1, 2, 3].filter(a => a > 1) // [2,3]

Reduce

The method reduce allows to process arrays returning one result as an accumulation of the values.

reduce(
    (accumulator, current) => nextAccumulatorValue, // reduce function (note implicit return)
    initialAccumulatorValue // initial value
)

The reduce function is executed on every element of the array. The 2 arguments of the

  • accumulator. It contains the initialAccumulatorValue on the first execution, the result of the previous execution on the next iterations.
  • current. The current element of the array.

Example:

[1, 2, 3].reduce((accumulator, current) => accumulator + current, 0) // returns 6

Here below every executions, the arguments and the returned value on each iteration:

  1. Arguments: (accumulator: 0, current: 1) -> returns 0 + 1 = 1
  2. Arguments: (accumulator: 1, current: 2) -> returns 1 + 2 = 3;
  3. Arguments: (accumulator: 3, current: 3) -> returns 3 + 3 = 6;

Note

If initialAccumulatorValue is undefined (or not passed), the execution will be different. Starting from the 2nd element of the array using 1st element of the array as accumulator).

  • (accumulator = 1, current = 2) returns 1 + 2
  • (accumulator = 3, current = 3) returns 3 + 3 = 6

In this case the results are equal, but it is only a case. In general this is not true. For instance you may have:

  • [1,2,3].reduce((a,b) => a + b + 1, 0); // returns 9
  • [1,2,3].reduce((a,b) => a + b + 1);    // returns 8

Exercise: array methods

You can compose several function in cascade to transform filter and process your arrays:

Example:

[1, 2, 3]
    .map(a => a + 1)
    .filter(a => a > 1)
    .reduce((a, b) => a + b, 0)

Solution:

[1, 2, 3]
    .map(a => a + 1) // returns [2, 3, 4]
        .filter(a => a > 1) // returns --> [ 2, 3, 4]
        .reduce((a, b) => a + b, 1)
            // (acc: 1, curr: 2) = 3
            // (acc: 3 + curr: 3) = 6
            // (acc: 6 + curr: 4) = 10

Object initialization

ES6 provides new notations for object initialization:

Shorthand property names

// Shorthand property names (ES2015)
let a = 'foo', b = 42, c = {};
let o = {a, b, c} // shorthand for {a: a, b: b, c: c}

Computed property names

// Computed property names (ES2015)
let prop = 'foo';
let o = {
[prop]: 'hey',
['b' + 'ar']: 'there'
}
// o = {foo: 'hey', bar: 'there'}

Spread operator

Spread operator (...) allows an iterable (arrays, objects) to expand in places where 0+ arguments are expected. Is widely used for array or objects.

Example for array:

var array = [3, 4]
// Spread operator for arrays
var newArray = [1, 2, ...array, 5]  // result ->[1,2,3,4,5] (new Array)

Here We can spread an array into a new array.

Example for object:

const obj = {
    b: "b",
    c: "c"
};

// Spread operator for objects
const newObj = {
    a: "a",
    ...obj, // spread obj into the new object
    d: "d" // define another attribute
};
// newObj = {
//    a: "a",
//    b: "b",
//    c: "c",
//    d: "d"
// }

Note

In case you spread an object with the same properties names, the spread operation are applied in order an override the old with the new one example:

const obj = {
    a: "b"
};

// Spread operator for objects
const newObj = {
    a: "a",
    ...obj // spread obj into the new object
};
// newObj = {
//    a: "b"
// }

Destructuring assignment

The destructuring assignment syntax is a JavaScript expression that makes it possible to unpack values from arrays, or properties from objects, into distinct variables.

Example for arrays:

const array = [1, 2, 3, 4];

const [c1, c2, ...rest] = array;
// c1=1,
// c2=2,
// rest = [3, 4]

Example for object:

var obj = {
    a: “a”,
    b: “b”,
    c: “c”
};
const { a, b: b1, ...rest } = obj;
// equivalent of
// a = "a";
// b1 = "b"
// rest= {c:”c”}

Classes

JavaScript Classes are templates for JavaScript Objects.

class Car {
    // constructor
    constructor(brand) {
        this.carName = brand;
    }
    // method
    present() {
        return 'I have a ' + this.carName;
    }
}


//creating an instance
const myCar = new Car("Ford"); // arguments for the constructor
myCar.present(); // "I have a Ford";
typeof Car; // "function"

A class, basically, is a special type of function.

We use the keyword class (sometimes called syntactic sugar) to better define the role of this function as a template for JavaScript Objects that should be used with the new construct.

You can also use the extends keyword to implement inheritance:

class Model extends Car {
    constructor(brand, mod) {
        super(brand); // call the "Car" constructor
        this.model = mod;
    }
    // add a new method
    show() {
        return this.present() + ', it is a ' + this.model;
    }
}
const mycar = new Model("Ford", "Mustang");
myCar.show(); // "I have a Ford, It's a Mustang"

You can define static properties and methods for classes:

class ClassWithStaticMethod {
    static staticProperty = 'someValue';
    static staticMethod() {
        return 'static method has been called.';
    }
}

console.log(ClassWithStaticMethod.staticProperty);
// output: "someValue"
console.log(ClassWithStaticMethod.staticMethod());
// output: "static method has been called."

Classes are often used for define React Components (before functional components and hooks was introduced).

Promise

An ES6 Promise is a pattern to represent and handle incomplete operations.

The Promise object represents the eventual completion (or failure) of an asynchronous operation and its resulting value.

var promise = new Promise(function (resolve, reject) {
    resolve(42); // here you can imagine ajax requests, setTimeout ...
});
promise.then((function (result) {
    console.log(result); // 42
}));

Chaining Promises

Trying to chain asynchronous operations with using the classical callback method, leads to the classic callback pyramid of doom:

doSomething(function(result) {
    doSomethingElse(result, function(newResult) {
        doThirdThing(newResult, function(finalResult) {
            console.log('Got the final result: ' + finalResult);
        }, failureCallback);
    }, failureCallback);
}, failureCallback);

With promises, if the function passed to then returns a Promise, it will be executed in chain. This allows write a more linear and readable code like the following:

doSomething()
    .then(function(result) {
        return doSomethingElse(result); // returns a promise
    })
    .then(function(newResult) {
        return doThirdThing(newResult); // returns a promise
    })
    .then(function(finalResult) {
        console.log('Got the final result: ' + finalResult);
    })
    .catch(failureCallback);

Limitations of Promise

There are some limitations of using promises:

  • no cancellation: the operation will be executed and will execute then (or catch) functions. There is no way to stop the execution (for instance an ajax request when the user press cancel). Some libraries (like axios) provide a cancellation extension, anyway this is not a feature of the Promise itserf.
  • single value: promises return only one single value and stop. This does not allow to manage streams of events (e.g. web sockets)
  • complex data flows: while it works perfectly for chaining simple operations, creating more complex data flows (conditional chaining and so on), may need a to still create some callback system.

Axios

Axios is a HTTP client library for the browser (and NodeJS) widely used, based on promises.

It provides several simple functions to do AJAX requests:

const ajaxCall = axios.get(“<URL>”); // axios.get returns a new Promise
ajaxCall.then((function (result) {
    console.log(result); // ajax response object
}));

The library is widely used in MapStore and it will be used for all the examples