JavaScript: Everything From ES2016 to ES2019

albertomontalesi

AlbertoM

Posted on January 5, 2020

JavaScript: Everything From ES2016 to ES2019

Hi, I am Alberto Montalesi, a full-stack self-taught developer. I create practical JavaScript tutorials and courses on my website inspiredwebdev.com to inspire other developers to grow and build the career that they want.

JavaScript is a language in constant evolution and in the past few years, many new features have been added to the ECMAScript specification.

This article is an extract of my book Complete Guide to Modern JavaScript and it covers the new additions of ES2016, ES2017, ES2018, ES2019 and was originally posted on my blog inspiredWebDev.

At the end of the article you will find a link to download a cheat sheet that summarizes everything.

Everything new in ES2016

ES2016 introduced only two new features:

  • Array.prototype.includes()
  • The exponential operator

Array.prototype.includes()

The includes() method will return true if our array includes a certain element, or false if it doesn't.

let array = [1,2,4,5];

array.includes(2);
// true
array.includes(3);
// false

Combine includes() with fromIndex

We can provide .includes() with an index to begin searching for an element. The default is 0, but we can also pass a negative value.

The first value we pass in is the element to search and the second one is the index:

let array = [1,3,5,7,9,11];

array.includes(3,1);
// find the number 3 starting from array index 1
// true
array.includes(5,4);
//false
array.includes(1,-1);
// find the number 1 starting from the ending of the array going backwards
// false
array.includes(11,-3);
// true

array.includes(5,4); returned false because, despite the array containing the number 5, it is found at index 2 but we started looking at position 4. That's why we couldn't find it and it returned false.

array.includes(1,-1); returned false because we started looking at the index -1 (which is the last element of the array) and then continued from that point onward.

array.includes(11,-3); returned true because we went back to the index -3 and moved up, finding the value 11 on our path.

The exponential operator

Prior to ES2016 we would have done the following:

Math.pow(2,2);
// 4
Math.pow(2,3);
// 8

Now with the new exponential operator, we can do the following:

2**2;
// 4
2**3;
// 8

It will get pretty useful when combining multiple operations like in this example:

2**2**2;
// 16
Math.pow(Math.pow(2,2),2);
// 16

Using Math.pow() you need to continuously concatenate them and it can get pretty long and messy. The exponential operator provides a faster and cleaner way of doing the same thing.

ES2017 string padding, Object.entries(), Object.values() and more

ES2017 introduced many cool new features, which we are going to see here.

String padding (.padStart() and .padEnd())

We can now add some padding to our strings, either at the end (.padEnd()) or at the beginning (.padStart()) of them.

"hello".padStart(6);
// " hello"
"hello".padEnd(6);
// "hello "

We specified that we want 6 as our padding, so then why in both cases did we only get 1 space?
It happened because padStart and padEnd will go and fill the empty spaces. In our example "hello" is 5 letters, and our padding is 6, which leaves only 1 empty space.

Look at this example:

"hi".padStart(10);
// 10 - 2 = 8 empty spaces
// "        hi"
"welcome".padStart(10);
// 10 - 6 = 4 empty spaces
// "   welcome"

Right align with padStart

We can use padStart if we want to right-align something.

const strings = ["short", "medium length", "very long string"];

const longestString = strings.sort(str => str.length).map(str => str.length)[0];

strings.forEach(str => console.log(str.padStart(longestString)));

// very long string
//    medium length
//            short

First, we grabbed the longest of our strings and measured its length. We then applied a padStart to all the strings based on the length of the longest so that we now have all of them perfectly aligned to the right.

Add a custom value to the padding

We are not bound to just add a white space as padding, we can pass both strings and numbers.

"hello".padEnd(13," Alberto");
// "hello Alberto"
"1".padStart(3,0);
// "001"
"99".padStart(3,0);
// "099"

Object.entries() and Object.values()

Let's first create an Object.

const family = {
  father: "Jonathan Kent",
  mother: "Martha Kent",
  son: "Clark Kent",
}

In previous versions of JavaScript we would have accessed the values inside the object like this:

Object.keys(family);
// ["father", "mother", "son"]
family.father;
"Jonathan Kent"

Object.keys() returned only the keys of the object that we then had to use to access the values.

We now have two more ways of accessing our objects:

Object.values(family);
// ["Jonathan Kent", "Martha Kent", "Clark Kent"]

Object.entries(family);
// ["father", "Jonathan Kent"]
// ["mother", "Martha Kent"]
// ["son", "Clark Kent"]

Object.values() returns an array of all the values whilst Object.entries() returns an array of arrays containing both keys and values.

Object.getOwnPropertyDescriptors()

This method will return all the own property descriptors of an object.
The attributes it can return are value, writable, get, set, configurable and enumerable.

const myObj = {
  name: "Alberto",
  age: 25,
  greet() {
    console.log("hello");
  },
}
Object.getOwnPropertyDescriptors(myObj);
// age:{value: 25, writable: true, enumerable: true, configurable: true}

// greet:{value: ƒ, writable: true, enumerable: true, configurable: true}

// name:{value: "Alberto", writable: true, enumerable: true, configurable: true}

Trailing commas in function parameter lists and calls

This is just a minor change to a syntax. Now, when writing objects we can leave a trailing comma after each parameter, whether or not it is the last one.

// from this
const object = {
  prop1: "prop",
  prop2: "propop"
}

// to this
const object = {
  prop1: "prop",
  prop2: "propop",
}

Notice how I wrote a comma at the end of the second property.
It will not throw any error if you don't put it, but it's a better practice to follow as it makes your colleague’s or team member’s life easier.

// I write
const object = {
  prop1: "prop",
  prop2: "propop"
}

// my colleague updates the code, adding a new property
const object = {
  prop1: "prop",
  prop2: "propop"
  prop3: "propopop"
}
// Suddenly, he gets an error because he did not notice that I forgot to leave a comma at the end of the last parameter.

Shared memory and Atomics

From MDN:

When memory is shared, multiple threads can read and write the same data in memory. Atomic operations make sure that predictable values are written and read, that operations are finished before the next operation starts and that operation is not interrupted.

Atomics is not a constructor, all of its properties and methods are static (just like Math) therefore we cannot use it with a new operator or invoke the Atomics object as a function.

Examples of its methods are:

  • add / sub
  • and / or / xor
  • load / store

Atomics are used with SharedArrayBuffer (generic fixed-length binary data buffer) objects which represent generic, fixed-length raw binary data buffer.

Let's have a look at some examples of Atomics methods:

Atomics.add(), Atomics.sub(), Atomics.load() and Atomics.store()

Atomics.add() will take three arguments, an array, an index, and a value and will return the previous value at that index before performing an addition.

// create a `SharedArrayBuffer`
const buffer = new SharedArrayBuffer(16);
const uint8 = new Uint8Array(buffer);

// add a value at the first position
uint8[0] = 10;

console.log(Atomics.add(uint8, 0, 5));
// 10

// 10 + 5 = 15
console.log(uint8[0])
// 15
console.log(Atomics.load(uint8,0));
// 15

As you can see, calling Atomics.add() will return the previous value at the array position we are targeting. when we call again uint8[0] we see that the addition was performed and we got 15.

To retrieve a specific value from our array we can use Atomics.load and pass two-argument, an array, and an index.

Atomics.sub() works the same way as Atomics.add() but it will subtract a value.

// create a `SharedArrayBuffer`
const buffer = new SharedArrayBuffer(16);
const uint8 = new Uint8Array(buffer);

// add a value at the first position
uint8[0] = 10;

console.log(Atomics.sub(uint8, 0, 5));
// 10

// 10 - 5 = 5
console.log(uint8[0])
// 5
console.log(Atomics.store(uint8,0,3));
// 3
console.log(Atomics.load(uint8,0));
// 3

Here we are using Atomics.sub() to substract 5 from the value at position uint8[0] which is equivalent to 10 - 5.
Same as with Atomics.add(), the method will return the previous value at that index, in this case, 10.

We are then using Atomics.store() to store a specific value, in this case, 3, at a specific index of the array, in this case, 0, the first position.
Atomics.store() will return the value that we just passed, in this case, 3. You can see that when we call Atomics.load() on that specific index we get 3 and not 5 anymore.

Atomics.and(), Atomics.or() and Atomics.xor()

These three methods all perform bitwise AND, OR and XOR operations at a given position of the array. You can read more about bitwise operations on Wikipedia at this link https://en.wikipedia.org/wiki/Bitwise_operation

ES2017 Async and Await

Keep reading... or just get the cheatsheets.


Thank you very much for reading. Follow me on DevTo or on my blog at inspiredwebdev or on twitter.

Disclaimer: Links to Amazon and Educative are affiliate links, purchases you make will generate extra commissions for me. Thank you


book banner

Get my ebook on Amazon and Leanpub

💖 💪 🙅 🚩
albertomontalesi
AlbertoM

Posted on January 5, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related