Today, in the eighth part of the translation of the JavaScript manual, we will review the language features that appeared in it after the release of the ES6 standard. We, in one way or another, have come across many of these opportunities before, somewhere dwelling on them in more detail, somewhere taking for granted. This section of the guide is intended, along with the disclosure of some topics that we have not previously dealt with, to streamline the knowledge of the novice developer in the field of modern JavaScript.
→
Part 1: first program, language features, standards→
Part 2: code style and program structure→
Part 3: Variables, Data Types, Expressions, Objects→
Part 4: Functions→
Part 5: Arrays and Loops→
Part 6: Exceptions, Semicolon, Pattern Literals→
Part 7: strict mode, this keyword, events, modules, mathematical calculations→
Part 8: ES6 feature overview→
Part 9: Overview of ES7, ES8, and ES9 Capabilities
About ES6
The ES6 standard, which it would be more correct to call ES2015 or ECMAScript 2015 (these are its official names, although everyone calls it ES6), appeared 4 years after the release of the previous standard - ES5.1. The development of all that is included in the standard ES5.1, took about ten years. Nowadays, everything that has appeared in this standard has become the familiar tools of a JS developer. It should be noted that ES6 made the most serious changes to the language (maintaining backward compatibility with its previous versions). In order to estimate the scale of these changes, it can be noted that the size of the document describing the ES5 standard is approximately 250 pages, and the ES6 standard is described in a document consisting of approximately 600 pages already.
')
The list of the most important innovations of the ES2015 standard can include the following:
- Arrow functions
- Promises
- Generators
let
and const
keywords- Classes
- Modules
- Pattern Literal Support
- Support for default function parameters
- Spread operator
- Destructuring assignment
- Empowering Object Literals
- Cycle
for...of
- Support for data structures
Map
and Set
Consider these possibilities.
Arrow functions
Arrow functions have changed the look and feel of JavaScript code. In terms of appearance, using them makes function declarations shorter and easier. Here is the declaration of the usual function.
const foo = function foo() {
But almost the same (although not completely similar to the above) switch function.
const foo = () => {
If the body of the arrow function consists of only one line, the result of which you need to return from this function, then it is written even shorter.
const foo = () => doSomething()
If the switch function takes only one parameter, you can write it as follows.
const foo = param => doSomething(param)
It should be noted that with the advent of switch functions, the usual functions have not gone away, they can still be used in the code, they work the same way as before.
Features of the keyword this in the switch functions
The switch functions do not have their own value of
this
; they inherit it from the execution context.
This eliminates the problem that, when using normal functions, had to be used, to save the context, to use constructions like
var that = this
. However, as was shown in the previous parts of the manual, this change has a serious effect on the features of working with switch functions and on the scope of their application.
Promises
Promises allow you to get rid of a well-known problem, called “hell of callbacks,” although their use implies the use of quite complex structures. This problem was solved in the ES2017 standard with the advent of
async/await
, which is based on promises.
JavaScript developers used promises even before the appearance of the ES2015 standard, using various libraries for this (for example, jQuery, q, deferred.js, vow). This indicates the importance and relevance of this mechanism. Different libraries implement it differently, the emergence of a standard in this area can be considered a very positive fact.
Here is the code written using callbacks (callbacks).
setTimeout(function() { console.log('I promised to run after 1s') setTimeout(function() { console.log('I promised to run after 2s') }, 1000) }, 1000)
Using promises, this can be rewritten as follows.
const wait = () => new Promise((resolve, reject) => { setTimeout(resolve, 1000) }) wait().then(() => { console.log('I promised to run after 1s') return wait() }) .then(() => console.log('I promised to run after 2s'))
Generators
Generators are special functions that can suspend their own execution and resume it. This allows, while the generator is in a standby state, to be executed by another code.
The generator independently decides that it needs to pause and allow another code, “waiting” for its turn, to execute. In this case, the generator has the opportunity to continue its execution after the operation, the results of which it is waiting for, will be completed.
All this is done thanks to the single simple keyword
yield
. When this keyword is found in the generator, its execution is suspended.
A generator can contain multiple lines with this keyword, suspending its own execution several times. Generators are declared using the
*function
construction. This asterisk in front of the word
function
should not be taken for something like a pointer dereference operator used in languages ​​like C, C ++ or Go.
Generators signify the emergence of a new JavaScript programming paradigm. In particular, they enable the two-way exchange of data between the generator and other code, allow you to create long-lived
while
loops that do not “hang” the program.
Consider an example illustrating the features of the generators. Here is the generator itself.
function *calculator(input) { var doubleThat = 2 * (yield (input / 2)) var another = yield (doubleThat) return (input * doubleThat * another) }
With this command we initialize it.
const calc = calculator(10)
Then we refer to its iterator.
calc.next()
This command starts an iterator, it returns such an object.
{ done: false value: 5 }
Here is the following. The code executes a function using the
input
value passed to the generator constructor. The generator code is executed until the
yield
keyword is found in it. At this point, it returns the result of dividing
input
by
2
, which, since
input
equals
10
, gives the number
5
. We get this number thanks to the iterator, and, along with it, an indication that the generator is not completed yet (the
done
property in the object returned by the iterator is set to
false
), that is, the function is only suspended.
The next time we call the iterator, we pass the number
7
to the generator.
calc.next(7)
In response, the iterator returns the following object.
{ done: false value: 14 }
Here, the number
7
was used to calculate the
doubleThat
value.
At first glance it may seem that the
input / 2
code is something like an argument of some function, but this is only the value returned in the first iteration. Here we skip this value and use the new input value
7
, multiplying it by
2
. After that we reach the second keyword
yield
, as a result, the value obtained at the second iteration is
14
.
At the next iteration, which is the last, we pass the number
100
to the generator.
calc.next(100)
In response, we get the following object.
{ done: true value: 14000 }
The iteration is completed (the
yield
keyword no longer occurs in the generator), the object returns the result of evaluating the expression
(input * doubleThat * another)
, that is,
10 * 14 * 100
and indicating that the iterator is finished (
done: true
).
Let and const keywords
In JavaScript, the
var
keyword has always been used to declare variables. Such variables have a functional scope. The
let
and
const
keywords allow you to declare, respectively, variables and constants that have block scope.
This means that, for example, a variable declared with the
let
keyword in a loop, inside an
if
block, or inside a normal code block, delimited by curly brackets, will not be outside this block. Variables declared with
var
are not held in such blocks, becoming available in the function at the level of which they are declared.
The
const
keyword works in the same way as
let
, but with its help it declares constants that are immutable.
In modern JS code, the
var
keyword is rarely used. It has given way to the keywords
let
and
const
. At the same time, which may seem unusual, the key word
const
used quite widely today, which indicates the popularity of the ideas of the immutability of entities in modern programming.
Classes
It happened so that JavaScript was the only extremely widespread language using the prototype inheritance model. Programmers switching to JS from languages ​​that implement a class-based inheritance mechanism felt uncomfortable in such an environment. The ES2015 standard has introduced class support in JavaScript. This is, in fact, “syntactic sugar” around JS internal mechanisms using prototypes. However, this affects exactly how JS applications are written.
Now JavaScript's inheritance mechanisms look like similar mechanisms in other object-oriented languages.
class Person { constructor(name) { this.name = name } hello() { return 'Hello, I am ' + this.name + '.' } } class Actor extends Person { hello() { return super.hello() + ' I am an actor.' } } var tomCruise = new Actor('Tom Cruise') console.log(tomCruise.hello())
This program displays the text
Hello, I am Tom Cruise. I am an actor
to the console
Hello, I am Tom Cruise. I am an actor
Hello, I am Tom Cruise. I am an actor
.
In JS classes, instance variables cannot be declared, they need to be initialized in constructors.
â–ŤConstructor class
Classes have a special method,
constructor
, which is called when creating an instance of a class using the
new
keyword.
SuperSword keyword super
The
super
keyword allows you to access the parent class from descendant classes.
â–ŤGetters and setters
The getter for a property can be set as follows.
class Person { get fullName() { return `${this.firstName} ${this.lastName}` } }
The setter can be described as shown below.
class Person { set age(years) { this.theAge = years } }
With getters and setters, they work as if they are not functions, but ordinary properties of objects.
Modules
Before the advent of the ES2015 standard, there were several competing approaches to working with modules. In particular, we are talking about the technologies RequireJS and CommonJS. This situation led to controversy in the community of JS-developers.
Nowadays, thanks to the standardization of modules in ES2015, the situation is gradually normalizing.
Import modules
Modules are imported using the
import...from...
construction. Here are some examples.
import * as something from 'mymodule' import React from 'react' import { React, Component } from 'react' import React as MyLibrary from 'react'
â–Ť Export of modules
The internal mechanisms of the module are closed from the outside world, but all that it can offer to other modules can be exported from the module. This is done using the
export
keyword.
export var foo = 2 export function bar() { }
â–ŤTemplate Literals
Sample literals are a new way to describe strings in JavaScript. Here's what it looks like.
const aString = `A string`
In addition, using the syntax of pattern literals allows you to embed expressions in strings, interpolate them. This is done using a
${a_variable}
. Here is a simple example of its use:
const v = 'test' const str = `something ${v}`
Here is an example more complicated, illustrating the possibility of calculating any expressions and substituting their results into a string.
const str = `something ${1 + 2 + 3}` const str2 = `something ${foo() ? 'x' : 'y' }`
Using template literals makes it much easier to declare multi-line strings.
const str3 = `Hey this string is awesome!`
Compare this with what you had to do to describe multi-line strings while using the features available in the language before ES2015.
var str = 'One\n' + 'Two\n' + 'Three'
Default Function Parameters
Functions now support default parameters — if the corresponding arguments are not passed to them when calling functions.
const foo = function(index = 0, testing = true) { } foo()
Spread operator
The spread operator (extension operator) allows you to "expand" arrays, objects or strings. This operator looks like three dots (
...
). First, consider it on the example of an array.
const a = [1, 2, 3]
Here's how to create a new array based on this array.
const b = [...a, 4, 5, 6]
Here's how to create a copy of the array.
const c = [...a]
This operator works with objects. For example - here’s how to use it to clone an object.
const newObj = { ...oldObj }
By applying the spread operator to a string, you can convert it into an array, each element of which contains one character from this string.
const hey = 'hey' const arrayized = [...hey]
This operator, in addition to the above-described options for its use, is convenient to use when calling functions that expect a normal list of arguments, passing an array with these arguments to them.
const f = (foo, bar) => {} const a = [1, 2] f(...a)
Previously, this was done using a construct of the form
f.apply(null, a)
, but such code is more difficult to write and read worse.
Destructuring assignment
The destructive assignment technique allows, for example, to take an object, extract some values ​​from it, and place them in named variables or constants.
const person = { firstName: 'Tom', lastName: 'Cruise', actor: true, age: 54, } const {firstName: name, age} = person
Here the
firstName
and
age
properties are extracted from the object. The
age
property is written to the declared constant here with the same name, and the
firstName
property, after extraction, falls into the
name
constant.
Destructuring assignment is also suitable for working with arrays.
const a = [1,2,3,4,5] const [first, second, , , fifth] = a
In the
first
,
second
and
fifth
constants, the first, second, and fifth elements of the array, respectively, fall.
Empowering Object Literals
In ES2015, features for describing objects using object literals are greatly enhanced.
â–ŤSimplification of inclusion in variable objects
Previously, to assign any variable to the property of an object, you had to use the following construction.
const something = 'y' const x = { something: something }
Now the same can be done like this.
const something = 'y' const x = { something }
Prototypes
The object prototype can now be defined using the following construct.
const anObject = { y: 'y' } const x = { __proto__: anObject }
SuperSword keyword super
Using the
super
keyword, objects can access prototype objects. For example, to call their methods that have the same names as the methods of these objects themselves.
const anObject = { y: 'y', test: () => 'zoo' } const x = { __proto__: anObject, test() { return super.test() + 'x' } } x.test()
â–ŤCalculated property names
Calculated property names are formed during the creation of an object.
const x = { ['a' + '_' + 'b']: 'z' } x.a_b
Cycle for ... of
In 2009, in the ES5 standard,
forEach()
cycles appeared. This is a useful design, the disadvantages of which include the fact that it is very inconvenient to interrupt such cycles. The classic
for
loop in situations where the execution of the loop needs to be interrupted before it ends normally, turns out to be a much more adequate choice.
In ES2015, a
for...of
loop appeared, which, on the one hand, is characterized by short syntax and convenience
forEach
, and on the other hand, it supports the possibilities for early exit from the loop.
Here are a couple of examples of a
for...of
loop.
Map and Set data structures
In ES2015, the
Map
and
Set
data structures appeared (as well as their “weak”
WeakMap
and
WeakSet
, the use of which makes it possible to improve the work of the “garbage collector” - the mechanism responsible for memory management in JS engines). These are very popular data structures, which, before their official implementation, had to be imitated by the available means of the language.
Results
Today we reviewed the capabilities of the ES2015 standard, which have greatly influenced the current state of the language. Our next topic will be the features of ES2016, ES2017 and ES2018 standards.
Dear readers! What innovations of the ES6 standard seem to you the most useful?
