This publication is a translation of the article “Introduction to commonly used ES6 features” by Zell Liew posted here . The translation is divided into 2 parts. The translation of the 2nd part is here .
JavaScript has seriously evolved in the last few years. If you are learning a language in 2017 and are not concerned with ECMAScript 6 (ES6), then you miss the easy way to read and write on it.
Do not worry if you are not a proficient language. You do not need to know him perfectly to take advantage of the “bonuses” added to ES6. In this article, I would like to share eight features of ES6 that I use every day as a developer, which will help you more easily immerse yourself in the new syntax.
Translator’s note: the article presents seven features (“Promises” are discussed in a separate article).
First, ES6 is a huge JavaScript update. Here is a large list of features (thanks, Luke Hoban), if you are interested in what innovations have appeared:
Do not be afraid of such a large list of features of ES6. You are not required to know everything at once. Next, I will share eight features that apply every day. These include:
By the way, browsers support ES6 very well. Almost all features are provided natively if you write code for the latest browser versions (Edge, latest versions of FF, Chrome, and Safari).
Writing code on ES6 does not need a tool like Webpack. In the absence of support by the browser, you can always refer to the polyfill created by the community. Googling is enough.
So, we proceed to the first feature.
In ES5 (“old” JavaScript), it was customary to declare variables through the var
keyword. In ES6, this word can be replaced by let
and const
, two powerful keywords that make development easier.
First, we draw attention to the difference between let
and var
, in order to understand why let
and const
better.
First, consider the var
we are familiar with.
First, we can declare variables through the var
keyword. Once the declaration has occurred, the variable can be used anywhere in the current scope.
var me = 'Zell' ; console.log(me); // Zell
In the example above, the global variable me
declared. This variable can also be used in a function. For example:
var me = 'Zell'; function sayMe () { console.log(me); } sayMe(); // Zell
However, the reverse is not true. If a variable is declared in a function, it will be impossible to use it outside the function.
function sayMe() { var me = 'Zell'; console.log(me); } sayMe(); // Zell console.log(me); // Uncaught ReferenceError: me is not defined
So, we can say that var
“functional” scope. This means that whenever a variable is declared with a var
in a function, it will exist only within the function.
If a variable is created outside the function, it will exist in the outer scope.
var me = 'Zell'; // function sayMe () { var me = 'Sleepy head'; // console.log(me); } sayMe(); // Sleepy head console.log(me); // Zell
In let
, on the other hand, the scope is “block” . This means that whenever a variable is declared with let, it will only exist within the block.
Wait, but what does “block” mean?
A block in JavaScript is what is inside a pair of curly braces. The following are examples of blocks:
{ // } if (true) { // } while (true) { // } function () { // }
The difference between variables declared “block” and “functionally” is huge. When declaring a variable “functionally,” in the future, you can randomly rewrite the value of this variable. Below is an example:
var me = 'Zell'; if (true) { var me = 'Sleepy head'; } console.log(me); // 'Sleepy head'
This example shows that the value of me
becomes 'Sleepy head'
after executing the code inside the if
block. Such an example, most likely, will not cause any problems, since it is unlikely that variables with the same name will be declared.
However, anyone who will work with var
through a for
loop may end up in a strange situation because of the way variables are declared. Imagine the following code, which displays the variable i
four times, and then outputs i
using the setTimeout
function.
for (var i = 1; i < 5; i++) { console.log(i); setTimeout(function () { console.log(i); }, 1000) };
What result can I expect when executing this code? Below is what actually happens:
i will be displayed four times with the value "5" in the timer function
How did i
get the value “5” four times inside the timer? This is because var
defines the variable "functionally", and the value of i
becomes equal to "4" even before the timer starts to run.
In order to get the correct value of i
inside setTimeout
, which will be executed later, you need to create another function, say logLater
, to ensure that the value of i
does not change in the for
loop before you start running setTimeout
:
function logLater (i) { setTimeout(function () { console.log(i); }); } for (var i = 1; i < 5; i++) { console.log(i); logLater(i); };
i is correctly displayed as 1, 2, 3 and 4
By the way, this is called closure.
The good news is that the "weirdness" of the "functionally" defined scope, shown in the for example with the for
loop, does not happen with let
. The same example with a timer can be rewritten so that it will work without adding additional functions:
for (let i = 1; i < 5; i++) { console.log(i); setTimeout(function () { console.log(i); }, 1000); };
i is correctly displayed as 1, 2, 3 and 4
As you can see, the “block” declared variables, bypassing the shortcomings of the “functionally” declared variables, greatly simplify development. For simplicity, I advise you to use let
instead of var
from this point on every time you need to declare variables in JavaScript.
Having understood what makes a let
, consider the difference between let
and const
.
Like let
, const
also has a “block” defined scope. The difference is that the value of const
cannot be changed after the announcement.
const name = 'Zell'; name = 'Sleepy head'; // TypeError: Assignment to constant variable. let name1 = 'Zell'; name1 = 'Sleepy head'; console.log(name1); // 'Sleepy head'
Since const
cannot be changed, it can be used for variables that will not change .
Suppose there is a button on the site that launches a modal window, and we know that this button is one and will not change. In this case, use const
:
const modalLauncher = document.querySelector('.jsModalLauncher');
When declaring variables, always give preference to const
instead of let
, because get an additional mark that the variable will not be reassigned. For other cases, use let
.
Next, consider the arrow function.
Arrow functions are indicated by an arrow (=>), which can be seen everywhere in the code on ES6. This abbreviation is used to create anonymous functions. They can be used wherever the function
keyword is present. For example:
let array = [1,7,98,5,4,2]; // ES5 var moreThan20 = array.filter(function (num) { return num > 20; }); // ES6 let moreThan20 = array.filter(num => num > 20);
Arrow functions are quite convenient, since with their help, you can reduce the code, thereby reducing the places where errors can be hidden. Also, the code written on them is easier to understand if you are familiar with their syntax.
Consider the switch functions closer to learn to recognize and use them.
First, pay attention to the creation of functions.
In JavaScript, it is probably customary to create functions in the following way:
function namedFunction() { // } // namedFunction();
The second way to create functions is associated with writing an anonymous function and assigning it to a variable. To create an anonymous function, you must remove its name from the function declaration.
var namedFunction = function() { // }
The third way to create functions is to pass them directly as an argument to another function or method. This method is most common for anonymous functions. Below is an example:
// callback- button.addEventListener('click', function() { // });
Since the arrow functions in ES6 are an abbreviation for anonymous functions, they can be implemented anywhere anonymous functions are created.
Below is what it looks like:
// const namedFunction = function (arg1, arg2) { /* do your stuff */} // const namedFunction2 = (arg1, arg2) => {/* do your stuff */} // callback button.addEventListener('click', function () { // }) // callback button.addEventListener('click', () => { // })
Notice the similarities? Essentially, remove the function
keyword and replace it with an arrow => in a slightly different place.
So what is the essence of the switch functions? Only in replacing function
with =>?
In fact, it’s not just a matter of replacing function
with =>. The syntax of the arrow function can be changed depending on two factors:
The first factor is the number of arguments passed to the arrow function. If only one argument is passed, you can omit the parentheses in which the arguments are enclosed. If no arguments are required, then the parentheses can be replaced with underscore _
.
All of the following examples are valid arrow functions.
const zeroArgs = () => {/* */} const zeroWithUnderscore = _ => {/* */} const oneArg = arg1 => {/* */} const oneArgWithParenthesis = (arg1) => {/* */} const manyArgs = (arg1, arg2) => {/* */}
The 2nd factor is the need for an implicit return . Arrow functions, by default, automatically create the return keyword if the code is one line and not wrapped in curly brackets {} .
So, below are two equivalent examples:
const sum1 = (num1, num2) => num1 + num2 const sum2 = (num1, num2) => { return num1 + num2 }
These two factors can serve as a basis for reducing the code as with moreThan20
, which was presented above:
let array = [1,7,98,5,4,2]; // ES5 var moreThan20 = array.filter(function (num) { return num > 20; }); // ES6 let moreThan20 = array.filter(num => num > 20);
In conclusion, the switch functions are pretty good. Having spent a little time and experienced them, you can get used to them and use them everywhere.
Before you join the "supporters" of the switch functions, let's get acquainted with another feature of them, which causes a lot of confusion, namely the lexical this
.
this
is a unique keyword whose value changes depending on the context of the call. When called outside of the function, this
refers to the Window
object in the browser.
console.log(this); // Window
When this
called through a simple function, it refers to a global object. In the case of browsers, this
will always be Window
.
function hello () { console.log(this); } hello(); // Window
JavaScript always sets this
browser window when calling a simple function. In turn, this explains the fact that this
in the timer functions of the type setTimeout
points to Window
.
When you call this
in an object method , it refers to the object itself:
let o = { sayThis: function() { console.log(this); } } o.sayThis(); // o
When you call a constructor function, this
refers to the constructed object .
function Person (age) { this.age = age; } let greg = new Person(22); let thomas = new Person(24); console.log(greg); // this.age = 22 console.log(thomas); // this.age = 24
When used in the event handler, this
refers to the element that triggered the event.
let button = document.querySelector('button'); button.addEventListener('click', function() { console.log(this); // button });
As you can see from the situations above, the value of this
is determined by the function that calls it. Each function sets its own value of this
.
In the switch functions, this
never acquires a new value regardless of how the function is called. this
will always have the same value as this
in the code surrounding the switch function. By the way, lexical means referring (related), because of what, as I believe, lexical this
and got its name.
So, it sounds confusing, so consider a few real-life examples.
First, do not use the arrow functions to declare the methods of objects , since if necessary, you can no longer refer to the object through this
.
let o = { // notThis: () => { console.log(this); // Window this.objectThis(); // Uncaught TypeError: this.objectThis is not a function }, // objectThis: function () { console.log(this); // o } // objectThis2 () { console.log(this); // o } }
Second, the arrow functions may not be suitable for creating event handlers, since this
will no longer be assigned to the element to which the event handler is attached.
However, you can always get the right context for this
through event.currentTarget
. For this reason, it has been said that " they may not be suitable ."
button.addEventListener('click', function () { console.log(this); // button }); button.addEventListener('click', e => { console.log(this); // Window console.log(event.currentTarget); // button });
Thirdly, the lexical this
can be used in situations where the this
binding can suddenly change. An example is the timer function, in which you will not have to deal with this
, that
or self
nonsense.
let o = { // oldDoSthAfterThree: function () { let that = this; setTimeout(function () { console.log(this); // Window console.log(that); // o }) }, // doSthAfterThree: function () { setTimeout(() => { console.log(this); // o }, 3000) } }
Such an application is especially useful if you need to add or remove a class after some time:
let o = { button: document.querySelector('button'); endAnimation: function () { this.button.classList.add('is-closing'); setTimeout(() => { this.button.classList.remove('is-closing'); this.button.classList.remove('is-open'); }, 3000) } }
moreThan20
, use the arrow functions everywhere for the purity and brevity of the code, as in the example above with moreThan20
:
let array = [1,7,98,5,4,2]; let moreThan20 = array.filter(num => num > 20);
The default parameters in ES6 allow you to set default parameters when creating functions. Consider an example to see how useful this is.
Create a function that reads the name of the team player. If you write this function on ES5, you get:
function announcePlayer (firstName, lastName, teamName) { console.log(firstName + ' ' + lastName + ', ' + teamName); } announcePlayer('Stephen', 'Curry', 'Golden State Warriors'); // Stephen Curry, Golden State Warriors
At first glance, it seems that the code is all right. However, suddenly it will be necessary to announce a player who is not a member of any team?
The current code simply does not cope with the task, if you skip teamName
:
announcePlayer('Zell', 'Liew'); // Zell Liew, undefined
Obviously, undefined
is not a command name.
If a player does not belong to any of the teams, then the announcement of Zell Liew, unaffiliated
will make more sense than Zell Liew, undefined
. I agree?
To announcePlayer
announce Zell Liew, unaffiliated
, perhaps, as an option, pass the line unaffiliated
instead of teamName
:
announcePlayer('Zell', 'Liew', 'unaffiliated'); // Zell Liew, unaffiliated
Although this approach works, it is better to make an improvement to announcePlayer
by checking the availability of teamName
.
In ES5, the refactored code would be:
function announcePlayer (firstName, lastName, teamName) { if (!teamName) { teamName = 'unaffiliated'; } console.log(firstName + ' ' + lastName + ', ' + teamName); } announcePlayer('Zell', 'Liew'); // Zell Liew, unaffiliated announcePlayer('Stephen', 'Curry', 'Golden State Warriors'); // Stephen Curry, Golden State Warriors
Or, if you know ternary operators, you can choose a shorter version:
function announcePlayer (firstName, lastName, teamName) { var team = teamName ? teamName : 'unaffiliated'; console.log(firstName + ' ' + lastName + ', ' + team); }
In ES6, using the default parameters, you can add an equals sign = whenever you specify a parameter. With this approach, ES6 automatically assigns a default value when the parameter is not defined.
So in the code below, when teamName
not defined, then teamName
accepts the default unaffiliated
.
const announcePlayer = (firstName, lastName, teamName = 'unaffiliated') => { console.log(firstName + ' ' + lastName + ', ' + teamName); } announcePlayer('Zell', 'Liew'); // Zell Liew, unaffiliated announcePlayer('Stephen', 'Curry', 'Golden State Warriors'); // Stephen Curry, Golden State Warriors
Convenient, isn't it?
Pay attention to another point. If you want to activate the default value, you can pass undefined
manually. Such a manual transfer undefined
is in place when the default parameter is not the last argument of the function.
announcePlayer('Zell', 'Liew', undefined); // Zell Liew, unaffiliated
The above is what you need to know about the default settings. Simple enough and very convenient.
Source: https://habr.com/ru/post/340002/
All Articles