What new in each version of Modern Javascript - ECMAScript(ES) from ES6 - ES11š„
May 16, 2020
If I needed to choose only one programming language to master, Javascript would be my choice. With Javascript we are able to create everything from web applications, mobile apps, and server-side applications, even desktop applications using Electron.js. I think this should convince you to learn Javascript! In order to get started with Javascript you should know some of its history - thatās what this blog is for. At the time I write this blog post, Javascript is on version šES2020 (ES11)š already (ES is short for EcmaScript, the technical name for Javascript).
A brief overview of what is new in all ES versions:
ES2015 (ES6)
In 2015, Javascript was back in the game after it had been ignored by most serious programmers (see the Why is Javascript bad? discussion on quora). 2015 was the golden year of Javascript and had its biggest update of all time.
These are the main updates of ES2015:
- Let/Const Variable Declaration
- Arrow Functions
- Classes
- Modules (import/export)
- Template Strings
- Default function parameters
- Rest/Spread Operators
- Array/Object Destructuring
- Promises
- Iterators and Generators
Let/Const
let
and const
variable declarations were introduced. It is highly recommended to use them over var
to be explicit about the nature of the variable.
let message = 'hello';message = 'good bye'; // works just fine because let declares variables that can be altered
const message = 'hello';message ='good bye'; // returns an error because the value of the Constant message cannot be changed
Arrow functions
With the old way, we would write functions as below:
function sum(value1, value2){return value1 + value2;}
But with arrow function we can write in a compressed way, like this:
const sum = (value1, value2)=>{return value1 + value2;}
The arrow syntax ()=>{}
creates a function and the constant sum
is declared to store it. This is also an example of an anonymous function
because it has no explicit name, but is immediately assigned to a variable.
We can use an even more simple syntax if the operation we are performing immediately returns a value:
const sum = (value1, value2) => value1 + value2
Classes
In ES6 we are able to write Javascript in with Object Oriented Programming (OOP) principles.
For example, we create new class called Calculator
(convention encourages the use of a capital letter when creating a class, but it isnāt required). The Calculator class constructor is called whenever the new
keyword is used to make an instance of the class, and expects one parameter called moneyBeforeDiscount
.
class Calculator{constructor(moneyBeforeDiscount){this.moneyBeforeDiscount = moneyBeforeDiscount;}moneyAfterDiscount(discountRate){return this.moneyBeforeDiscount * (100-discountRate)/100;}}
We instantiate the Calculator
class to a variable called bill
and call one of its method to print out the total after discount.
const bill = new Calculator(5000);console.log('Total after discount :' + bill.moneyAfterDiscount(5));
We can also use class inheritance with the keyword extend
and reference back to parent class with the keyword super
.
class MoreCalculate extend Calculator{constructor(moneyBeforeDiscount,discountRate,moneyExchange){super(moneyBeforeDiscount);this.moneyExchange = moneyExchange;this.discountRate = discountRate;}moneyAfterExchange(){return this.moneyAfterDiscount(this.discountRate)/this.moneyExchange;}}
Modules (import/export)
Before ES6, Javascript didnāt support module management natively. In ES6 though, we can export
functions and variables from a file, and then import
those elements in a different file.
// calculator.jsconst add = (a, b) => a + bconst multiply = (a, b) => a * bexport { add, multiply }
// App.jsimport { add } from './calculator'console.log(add(2, 3)); //5
Template Strings
Declaring strings is often done on one line using either single quotes '
or double quotes "
. The backtick character ` allows us to create template strings and replace repetative +
string operations to concatenate regular strings and variables as well as allow multiline string variables. In order to interpolate variables inline just wrap the variable name in ${}
:
const sum = 2+5;console.log(`2 + 5 = ${sum}`);//normal methodconsole.log('2 + 5 ='+ sum);
You can also call functions and perform operations inside the template wrapper:
const message = 'khmer coder';console.log(`good morning, ${message} and ${2+2}`);//good morning, khmer coder and 4;
Default function parameter values
ES6 also includes the ability to set a default parameter value which allows developers to set a fallback value if a function is called without necessary parameters.
const greeting = (name, message = 'Hello') => console.log(`${message}, ${name}`);greeting('Sambat'); // "Hello, Sambat"greeting('khmer coder','Hi'); // "Hi, khmer coder"
Rest/Spread Operators
The Rest Operator (not related to REST API models) is used when there is an unknown or unlimited number of parameters in calling a function. By declaring ...
before a variable name in a function definition it will create an iterable variable of any length:
function add(...numbers) {sum = 0for(const n of numbers) {sum += n}return sum}console.log(add(1, 2, 3)) // 6
The Spread Operator is used to āspread outā an array by returning the individual values in order by entering ...
before a variable. One use of this is to insert one array into the center of another, like:
const a = [4, 5, 6]const b = [1, 2, 3, ...a, 7] // [1, 2, 3, 4, 5, 6, 7]
Array/Object Destructuring
Array and Object Destructuring are used when we want to assign the value of variables from somewhere in an array or object directly. When destructuring, the variables on the left of the assignment operator are updated but the Array or Object is left unchanged.
Array destructuring uses the order of the array to return a value and assigns it to the variable in the same position on the left side of the assignment operator.
const studentScore = [50, 25, 79]const [first, second] = studentScoreconsole.log(first, second) // 50, 25
Object destructuring matches the variable names matched to the Objectās properties to perform the assignment.
const studentA = {name: 'A',score: 99}const { name, score } = studentAconsole.log(`${name}: ${score}`) // A: 99
Promises
Promises are widely used in asynchronous programming to handle HTTP requests and other long running processes. Promises return a result with to a function declared with the .then
method, and .then
s can be chained to perform multiple process steps. Error handling is done with the .catch
method calling its own function. All Promise handler functions can be declared anonymously inline.
url = 'https://api.github.com/search/users?q=sambatlim'fetch(url).then(response => response.json()).then(result => {console.log(result)})
Iterators and Generators
Iterators:
ES6 provided the new looping syntax for x of y
, which is similar to foreach
in most programming languages.
let arr= [a,b,c];for(let value of arr){console.log(value);}//output: a, b, c
Generators:
Generators can return (yield
) multiple values, one after another, on demand. They work great with Iterators.
To create the generator we need the special syntax construct: function*
to explicity declare a normal function a generator.
To get a value we need to use the generator method next()
. next()
will always return an Object with two properties:
value
: the value that isyield
ed orreturn
eddone
:true
if the function code has finished, otherwisefalse
When It reached the return
statement, the function will finish and return done
:true
.
function* generateSequence() {yield 1;yield 2;return 3;}let generator = generateSequence();let one = generator.next();console.log(JSON.stringify(one));//output: {value: 1, done: false}let two = generator.next();console.log(JSON.stringify(two));//output: {value: 2, done: false}let three = generator.next();console.log(JSON.stringify(three));//output: {value: 3, done: true}//function has finished because it reached the return statement.
ES2016 (ES7)
ES2016 introduced the minor update from the ES6 such as the Array method .includes()
to check for the presence of a value, and an exponential operator **
.
Array.includes()
const number = [1,2,3]console.log(number.includes(1)); //trueconsole.log(number.includes(7)); //false
Exponential Operator (**
)
console.log(2 ** 3) // 8
ES2017 (ES8)
ES8 helps us to write the asynchronous functions in a more convenient way by introducing the keywords async/await
. We can use these to avoid callback hell (a chain of dependant callback functions that can be very hard to debug) and improves the readability of Promises.
Async Functions (async/await)
With ES6 Promise handling we would write
fetch('https://api.github.com/search/users?q=sambatlim').then(response => {// callback 1response.json()).then(result => {// callback 2console.log(result)})
With ES2017 we can declare an overall function as async
, and then explicilty use await
within it to resolve Promise values without blocking the whole Javascript runtime, but maintaining the normal way to write and read a Javascript function.
async function getgithubprofile(){const response = await fetch('https://api.github.com/search/users?q=sambatlim') // response isn't assigned until fetch returnsconst result = await response.json() // result is not assigned until response is declared and json() returns a valueconsole.log(result)}
recommend to read more here: article by Javascript info.
ES2018 (ES9)
In this update, ES2018 introduced nothing new but increased the ability of the Rest and Spread Operators (...
) that were introduced in ES6. Again, the original Object is unaltered.
Rest Operator for Objects
Now we can use rest on the properties of an Object. It allows us to explicitly extract certain named variables, and assign any uncalled variables into a catchall Object.
const options = {enabled: true,text: 'Hello',color: 'red'}const { enabled, ...others } = optionsconsole.log(enabled) // trueconsole.log(others) // { text: 'Hello', color: 'red' }
Spread Operator for Objects
Using the spread operator during an Object declaration will assign the properties of the referenced Object to the new Object.
const moreOptions = {text: 'Hello',color: 'red'}const options = { enabled: true, ...moreOptions }console.log(options) // { enabled: true, text: 'Hello', color: 'red' }
ES2019 (ES10)
An update for developer convience allows the use of try/catch
without an explicit e
Error reference in the catch
call.
// Beforetry {fetch('https://example.com')} catch (e) {console.log(`e`)}
// Aftertry {fetch('https://example.com')} catch {console.log(`something wrong.`)}
š„ES2020 (ES11)
ES2020 introduced:
- Dynamic Import
- BigInt
- Nullish Coalescing Operator(
??
) - Optional Chaining (
?.
)
Dynamic Import
In ES6 when we want to import references we need to write import
at the beginning of the file. This is a static import and it cannot be conditional.
With dynamic imports we can easily assign what modules we need to import on demand, which is very useful for big applications that can have a lot of modules.
let stageId = 'A'let stageif (stageId === 'A') {stage = await import('./stages/A')} else {stage = await import('./stages/B')}stage.run()
read more at: v8.dev.
BigInt
BigInt is a Javascript data type that can store numbers larger than 253 - 1. To create a BigInt you append an n
to the actual number.
const big = 123456789n
or convert to a BigInt by calling BigInt(yourNumber)
.
const alsoBig = BigInt(20)
Warning: The operators +
, -
, *
, /
can only be used on combinations of BigInt variables.
Nullish Coalescing Operator (??)
Before introducing you to the Nullish Coalescing Operator, lets talk about the way you write code to check for null
or undefined
values. If we wanted to check the value of score
for example, you would usually write:
let score = nullconsole.log(score || 'No score') //this code will print 'No score'
The problem with this is it will check not only for undeclared values like null
or undefined
, but for āfalsyā values like Boolean false
, 0
, ""
(empty string), or NaN
(Not a Number) and treat them as empty values as well:
let score = 0console.log(score || 'No score') // this will print 'No score' instead of 0;
The introduction of the nullish coalescing operator ??
has come to help! It will evaluate the left hand argument strictly for null
or undefined
, allowing āfalsyā values to be returned.
let score = 0console.log(score ?? 'No score') //will print 0;
Optional Chaining (?.)
Letās imagine you have an Object nested within another Object:
const student ={profile:{school:{name:'RUPP'}}}
If you want to get the name
property of the student
ās school
property, you would normally use student.profile.school.name
. But what if student.profile
hasnāt been declared? You would get an error:
Uncaught TypeError: Cannot read property 'school' of undefined
So to prevent that error you could add an IF statement that checked for the existence of student.profile
before trying to reference its properties, so that it returns before erroring out if profile
is undefined:
if(student.profile && student.profile.school.name) {return student.profile.school.name;}
But that is long, and you should really check each level of property. Using the optional chaining syntax ?.
on the profile
property you can write the equivalent line:
student.profile?.school.name
This will prevent the error caused by an undeclared Object property.
ššThatās all for Whatās New in Javascript! See you next yearā¦šš