📜 ⬆️ ⬇️

Introduction to frequently used features of ES6. Part 1

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).


List of ES6 features


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:


  1. let + const
  2. Arrow functions
  3. Default parameters
  4. Destructuring
  5. Rest parameter and spread operator
  6. Extended Object Literals
  7. Pattern lines
  8. Promises

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.


Let and const


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.


Let vs var


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 .


Let vs 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


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.


Learn more about switch functions.


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:


  1. required number of arguments
  2. need for implicit return

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 .


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); 

Default settings


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