Core features of next-generation JavaScript

Reading Time: 4 minutes

Since we are working with the modern frameworks or libraries of course it is strongly recommended to use the next-generation JS features. Let’s take an overview of the most used features together.

As you know we do not use anymore var but instead, we are using let or const, depends on the case scenario.

Let

Let is a block-scoped local variable which makes the variable limited to the scope of the block statement.

Example:

function varTest() {
  var x = 1;
  {
    var x = 2;  // same variable!
    console.log(x);  // 2
  }
  console.log(x);  // 2
}

function letTest() {
  let x = 1;
  {
    let x = 2;  // different variable
    console.log(x);  // 2
  }
  console.log(x);  // 1
}

Const

Similar like let, const is also block-scoped, but here the difference is that a constant cannot be reassigned or redeclared. So, from here we can say they are read-only objects.
What is interesting here is that the value is mutable. That means direct reassignment is not allowed but changing object properties.

const ctest = 1;
ctest = 2;	// results with error

const cobj = {
    name: "Dimitar"
}
cobj.name = "Test"; // no errors

Arrow functions

Arrow functions are a replacement of the standard known normal functions which give us new shorter syntax and different behaviour of the scope where:

  • When calling normal functions this refers to the object that calls the function
  • When calling arrow function this refers to the outer function that surrounds the inner function
function thisTest() {
    let that = this
    this.prop1 = 0;

    setInterval(function growUp() {
        this.prop1++;
		that.prop1++;
        console.log(this.prop1)
        console.log(that.prop1)
    }, 1000)
}

function thisTest1() {
    let that = this
    this.prop1 = 0;

    setInterval(() => {
        console.log(this.prop1)
        console.log(that.prop1)
        this.prop1++;
		that.prop1++;
    }, 1000)
}

let thisNormal = new thisTest();
/* Prints:
undefined 0
NaN 1
NaN 2
NaN 3
...
*/
let thisArrow = new thisTest1();
/* Prints:
0 0
2 2
4 4
6 6
...
*/

Exports & Imports

In every modern project, we are splitting the code of our project in different modules, where those modules keep our code module focused and manageable. So, the communication between the modules, we maintain with imports (used to get access to a module) and exports (used to make it available to other modules).

From here we can export or import everything, from constants to classes by having unlimited exports and imports.

Exports:
export let numbers = [1, 2, 3 , 4, 5]
export class User {
	constructor(name) {
		this.name = name
}
}

Imports:
import { User } from ‘./…’

Classes

…are used to replace constructor functions and provide better readability.

class P {
    constructor() {
        name = "Dime"
    }
}

const p = new P();
p.name;


replaced with

class P2 {
     constructor () {
         this.name = 'Max';
     }
}

const p2 = new P2();
console.log(p2.name);

class Human {
    species = 'human';
}

class Person extends Human {
    name = 'Max';
    printMyName = () => {
        console.log(this.name);
    }
}

const person = new Person();
person.printMyName();
console.log(person.species);

Spread operator

Spread operator allows us to pull elements out of an array or pull the properties of an object. The spread operator is very useful because it is a lot used to help us clone arrays and objects with different reference.

const arr1 = [1, 2, 3];
const arr2 = [...arr1, 4, 5];

const obj1 = {
    name: "Dime"
}

const obj2 = {
    ...obj1,
    age: 26
}

const obj3 = {...obj1};
const obj4 = obj1;
obj1 === obj3;  // false
obj1 === obj4; // true

Destructuring

The last next-generation feature I am going to cover is destructuring. Destructuring allows us to easily access the values of arrays and objects and assign them to variables. This is mostly used when we are working with function arguments when we are calling with the whole object but getting the properties that we only need.

const arr = [1, 2, 3];
const [a1, a2] = arr;

const obj = {
    name: "Dime",
    age: 26
}

const {name} = obj;

const printValue = (obj) => {
    console.log(obj.name);
}

const printValue1 = ({name}) => {
    console.log(name);
}

printValue({name: "Dime", age: 26});
printValue1({name: "Dime", age: 26});

Conclusion

As we can see next generation is already here and I strongly believe that in near future in the modern projects there won’t be tolerated non-next-generation JavaScript code.
So don’t hesitate, simply just try it and enjoy the next-generation JavaScript features.

What to expect from ECMAScript in 2019

Reading Time: 5 minutes

JavaScript as a language has been maturing at a steady pace in the past decade with the evolution of ECMAScript. The current version was released in June 2018, ES2018 and the upcoming version brings several new additions to the language which we’ll cover in this post.

Before that, it can be useful to know how new features are being added to the language through the additions to the ECMAScript standard.

ECMAScript proposals

The Technical Committee 39, TC39, is responsible for reviewing and adopting changes to the existing version of the ECMAScript standard. The process takes the form of 5 stages of maturity after which a new feature is included in the standard.

  • Stage 0: Strawman
  • Stage 1: Proposal
  • Stage 2: Draft
  • Stage 3: Candidate
  • Stage 4: Finished

There is no guarantee that a feature in Stage 3 will be included in the standard, but generally, JavaScript engine implementations like V8 (Chrome, Node.js, Opera), SpiderMonkey (Firefox) and Chakra (Microsoft Edge) tend to add some features with experimental support so that developers can test and provide feedback.

ES2019 finished features

At the time of writing this post, there have been several finished features, you can check the full list in the TC39 proposal repository.

  • Array.prototype.{flat, flatMap}
  • Object.fromEntries()
  • String.prototype.{trimStart, trimEnd}
  • Symbol.prototype.description
  • Subsume JSON
  • Optional catch binding
  • Well-formed JSON.stringify()
  • Function.prototype.toString()

Array.prototype.{flat, flatMap}

The new array instance method, flat(), flattens an array up to a given value, it takes an optional depth argument which can be set to Infinity to run recursively until the array is single dimensional.

const nestedArray = ["a", ["b", "c"], ["d", ["e", ["f"]]]];

// using flat() on a multidimensional array
nestedArray.flat();
["a", "b", "c", "d", ["e", ["f"]]]

// using flat() with an argument
nestedArray.flat(2);
["a", "b", "c", "d", "e", ["f"]]

// using flat() with Infinity as argument
nestedArray.flat(Infinity);
["a", "b", "c", "d", "e", "f"]

flatMap() is similar to the existing map() method, but the callback can return an array and the end result will be a single dimensional array instead of nesting arrays.

const scatteredArray = ["a b c", "d", "e f", "g"];

// map() results in nested arrays
scatteredArray.map( chunk => chunk.split(" "));
[["a", "b", "c" ], [ "d" ], [ "e", "f" ], [ "g" ]]

// flatMap() concatenates the returned arrays together
scatteredArray.flatMap( chunk => chunk.split(" "));
["a", "b", "c", "d", "e", "f", "g"]

Object.fromEntries()

Objects have an entries() method since ES2017, it returns an array key-value pairs of the object’s properties. The fromEntries() method does the reverse and returns an object from an array of properties.

If we use both methods on an object the cloned object will be copied by reference.

const person = { name: "John", age: 30, country: "UK" }

// using Object.entries and assigning it to a new object
const entries = Object.entries(person)
[["name", "John" ], [ "age", 30 ], [ "country, "UK" ]]

// using the new Object.fromEntries
const newPerson = Object.fromEntries(entries)
{ name: "John", age: 30, country: "UK" }

// the new object is a shallow copy
person !== newPerson
true

String.prototype.{trimStart, trimEnd}

trimStart() returns a string with removed whitespaces from the start of the original string, while trimEnd() removes whitespaces from the end.

const str = "   blank spaces   ";

// trimStart() usage
str.trimStart();
"blank spaces   "

// trimEnd() usage
str.trimEnd();
"   blank spaces"

Symbol.prototype.description

When creating a symbol, you can now retrieve its description property instead of using toString() on the object.

const symbol = Symbol("This is the symbol description")

// retrieving the description property
symbol.description;
"This is the symbol description"

Subsume JSON

This proposal solves an issue of inconsistency between a JSON and an ECMAScript JSON, which by specification should be a superset of a JSON.

JSON strings currently accept unescaped U+2028 and U+2029 characters, while ECMAScript strings don’t. With this extension of the ECMAScript JSON, this gap will be patched.

Optional catch binding

In previous versions of ECMAScript, we had to implement a catch binding even if we never had to use it.

try {
  // ...
} catch (error) {
  // handle error
}

With this addition, we can now simply omit it.

try {
  // ...
} catch {
  // handle error
}

Well-formed JSON.stringify()

This fixes the JSON.stringify() output when it processes surrogate UTF-8 code points (U+D800 to U+DFFF). Before this change calling JSON.stringify() would return a malformed Unicode character (a “�”).

Now those surrogate code points can be safely represented as strings using JSON.stringify() and transformed back into their original representation using JSON.parse().

Function.prototype.toString()

ES2019 introduces a change to the toString instance method of functions. Now the returned value will be exactly as defined, without stripping whitespaces and comments.

function /* this is foo */ foo () {}

// previous behavior of toString()
foo.toString()
"function foo () {}"

// proposal behavior
foo.toString()
"function /* this is foo */ foo () {}"

Upcoming candidate features

The following list contains all current candidate features which have reached Stage 3 and will most likely be included in the specification in the future.