📜 ⬆️ ⬇️

Var, let or const? Problems scopes variables and ES6

Scopes in JavaScript have always been a difficult topic, especially when compared to more strictly organized languages, such as C and Java. For many years, the areas of visibility in JS have not been widely discussed, since the language simply did not have the means to significantly influence the current situation. But in ECMAScript 6 some new features have appeared that allow developers to better control the scopes of variables. These features nowadays support browsers very well, they are quite accessible to most developers. However, new keywords for declaring variables, given the fact that the old var keyword has not gone away, mean not only new opportunities, but also the emergence of new questions. When to use the keywords let and const ? How do they behave? In what situations is the var keyword still relevant? The material, the translation of which we are publishing today, is aimed at studying the problem of the scope of variables in JavaScript.



Variable scope: overview


The scope of a variable is an important programming concept that, however, may confuse some developers, especially newbies. The scope of a variable is the part of the program where this variable can be accessed.

Take a look at the following example:
')
 var myVar = 1; function setMyVar() { myVar = 2; } setMyVar(); console.log(myVar); 

What will the console.log method display? The answer to this question will not surprise anyone: it will output 2 . The variable myVar declared outside of any function, which tells us that it is declared in the global scope. Therefore, any function declared in the same scope can refer to myVar . In fact, if we are talking about code executed in the browser, functions declared in other files connected to the page will even have access to this variable.

Now take a look at the following code:

 function setMyVar() { var myVar = 2; } setMyVar(); console.log(myVar); 

Externally, its changes, compared with the previous example, are insignificant. Namely, we just put the variable declaration inside the function. What will console.log display now? In fact, nothing, since this variable is not declared, and when you try to access it, you will get a message about the raw ReferenceError error. This is because the variable, using the var keyword, is declared inside the function. As a result, the scope of this variable is limited to the internal scope of the function. It can be accessed in the body of this function; functions embedded in this function can work with it, but it is not available from the outside. If we need a certain variable to be used by several functions that are on the same level, we need to declare this variable in the same place where these functions are declared, that is, one level above their internal scope.

Here is one interesting observation: the code of most websites and web applications does not apply to the work of any one programmer. Most software projects are the results of team development, and, moreover, they use third-party libraries and frameworks. Even if one programmer is involved in the development of a certain site, he usually uses external resources. Because of this, it is usually not recommended to declare variables in the global scope, since it is not possible to know in advance which variables will be declared by other developers whose code will be used in the project. In order to circumvent this problem, you can use some techniques, in particular, the “ Module ” and IIFE patterns when applying an object-oriented approach to JavaScript development, although the same effect allows you to achieve data and function encapsulation in ordinary objects. In general, it can be noted that variables, the scope of which goes beyond the limits that they need, usually represent a problem with which to do something.

Var keyword problem


So, we figured out the concept of "scope". We now turn to more complex things. Take a look at the following code:

 function varTest() { for (var i = 0; i < 3; i++) {   console.log(i); } console.log(i); } varTest(); 

What gets to the console after it is executed? It is clear that inside the loop the values ​​of the increasing counter i will be displayed: 0 , 1 and 2 . After the cycle ends, the program continues to run. Now we are trying to access the same counter variable that was declared in the for loop, outside of this loop. What will come of it?

After accessing i outside the loop, the console will get 3, since the var keyword is valid at the function level. If you declare a variable using var , then you can access it in a function after exiting the construction where it was declared.

This can become a problem when the functions become more complex. Consider the following example:

 function doSomething() { var myVar = 1; if (true) {   var myVar = 2;   console.log(myVar); } console.log(myVar); } doSomething(); 

What gets to the console now? 2 and 2 . We declare the variable, initialize it with the number 1, and then try to override the same variable inside the if expression. Since these two declarations exist in the same scope, we cannot declare a new variable with the same name, even though we obviously want to do just that. As a result, the first variable is overwritten inside an if expression.

This is the biggest disadvantage of the var keyword. The scope of variables declared using it is too large. This can lead to inadvertent overwriting of data and other errors. Large scopes often lead to sloppy programs. In general, a variable should have a scope that is limited by its needs, but not exceeding them. It would be good to be able to declare variables whose scope is not as large as when using var , which would allow, if necessary, to use more stable and better error-protected software constructs. In fact, ECMAScript 6 provides us with such opportunities.

New ways to declare variables


The ECMAScript 6 standard (a new set of JavaScript capabilities, also known as ES6 and ES2015) gives us two new ways of declaring variables that are limited, compared to var , with a scope and some other features. These are the let and const keywords. Both that and another gives us a so-called block scope. This means that when used, the scope can be limited to a block of code, such as a for loop or an if expression. This gives the developer more flexibility in the choice of scopes of variables. Consider new keywords.

▍Using the let keyword


The let keyword is very similar to var , the main difference is the limited scope of variables declared with it. Rewrite one of the above examples, replacing var with let :

 function doSomething() { let myVar = 1; if (true) {   let myVar = 2;   console.log(myVar); } console.log(myVar); } doSomething(); 

In this case, the numbers 2 and 1 will go to the console. This happens because the if expression specifies a new scope for a variable declared with the let keyword. This leads to the fact that the second declared variable is a completely independent entity, not connected with the first one. You can work with them independently. However, this does not mean that nested blocks of code, such as our if expression, are completely cut off from the variables declared with the let keyword in the scope in which they themselves are located. Take a look at the following code:

 function doSomething() { let myVar = 1; if (true) {   console.log(myVar); } } doSomething(); 

In this example, the number 1 will appear in the console. The code inside the if expression has access to the variable we created outside of it. Therefore, he displays its value in the console. What happens if you try to shuffle the scopes? For example, do this:

 function doSomething() { let myVar = 1; if (true) {   console.log(myVar);   let myVar = 2;   console.log(myVar); } } doSomething(); 

It may seem that the first call of console.log will display 1 , but in fact, when you try to execute this code, a ReferenceError error will appear, which tells us that the myVar variable for this scope is not defined or not initialized (the text of this error differs in different browsers). In JavaScript, there is such a thing as raising variables to the upper part of their scope. That is, if a variable is declared in some scope, JavaScript reserves space for it even before the command for its declaration is executed. How exactly this happens is different when using var and let .

Consider the following example:

 console.log(varTest); var varTest = 1; console.log(letTest); let letTest = 2; 

In both cases, we try to use the variable before it is declared. But the commands to output data to the console behave differently. The first, using a variable that will later be declared using the var keyword, will output undefined - that is, what will be written to this variable. The second command, which tries to access a variable that will later be declared using the let keyword, will issue a ReferenceError and tell us that we are trying to use the variable before it is declared or initialized. What's the matter?

But the point is that before executing the code, the mechanisms responsible for its execution look through this code, find out whether any variables will be declared in it, and, if so, they are lifted with space reserved for them. At the same time, variables declared with the var keyword are initialized to undefined within their scope, even if they are accessed before they are declared. The main problem here is that the value undefined in a variable does not always indicate that the variable is attempted to be used before it is declared. Take a look at the following example:

 var var1; console.log(var1); console.log(var2); var var2 = 1; 

In this case, although var1 and var2 declared differently, both console.log calls will display undefined . The point here is that the variables declared with var but not initialized are automatically written to the value undefined . In this case, as we have said, variables declared with var , which are accessed before they are declared, also contain undefined . As a result, if something goes wrong in such a code, it will not be possible to understand what exactly is the source of the error — the use of an uninitialized variable or the use of a variable prior to its declaration.

The place for variables declared with the let keyword is reserved in their block, but before they are declared they fall into the Temporal Dead Zone (TDZ). This leads to the fact that they, before their declaration, cannot be used, and an attempt to access such a variable leads to an error. However, the system knows the exact cause of the problem and reports this. This is clearly seen in this example:

 let var1; console.log(var1); console.log(var2); let var2 = 1; 

Here, the first call of console.log will display undefined , and the second will cause a ReferenceError error, telling us that the variable has not yet been declared or initialized.

As a result, if using var appears with undefined , we do not know the reason for this behavior of the program. A variable can either be declared and uninitialized, or it may not yet be declared in this scope, but will be declared in code that is located below the command to access it. When using the let keyword, we can understand what is happening, and this is much more useful for debugging.

▍Using the const keyword


The const keyword is very similar to let , but they have one important difference. This keyword is used to declare constants. Values ​​of constants after their initialization cannot be changed. It should be noted that this applies only to the values ​​of primitive types, water strings or numbers. If a constant is something more complicated, for example, an object or an array, the internal structure of such an entity can be modified, you cannot just replace it with another. Take a look at the following code:

 let mutableVar = 1; const immutableVar = 2; mutableVar = 3; immutableVar = 4; 

This code will be executed up to the last line. Attempting to assign a new value to a constant will result in a TypeError error. This is how constants behave, but, as already mentioned, the objects with which constants are initialized can be changed, they can undergo mutations, which can lead to surprises .

Perhaps you, as a JavaScript developer, wonder why immunity of variables is important. Constants are a new phenomenon in JavaScript, while they are an essential part of languages ​​like C or Java. Why is this concept so popular? The fact is that the use of constants makes us think about exactly how our code works. In some situations, a change in the value of a variable may disrupt the operation of the code, for example, if it contains the number Pi and it is constantly being accessed, or if the variable has a link to some HTML element that you need to work with all the time. Say, here is a constant in which a link to a certain button is written:

 const myButton = document.querySelector('#my-button'); 

If the code depends on the link to the HTML element, then we need to ensure that the link remains unchanged. As a result, we can say that the const keyword goes not only along the path of improvements in the scope of visibility, but also along the path of limiting the possibility of modifying the values ​​of constants declared using this keyword. Remember how we talked about the fact that a variable should have exactly the scope that it needs. This idea can be continued by putting forward a recommendation that the variable should only have the opportunity to change, which is necessary for proper work with it, and nothing more. Here is a good material on the topic of immunity, from which an important conclusion can be drawn, according to which the use of immutable variables forces us to think more carefully about our code, which leads to an improvement in the purity of the code and to a decrease in the number of unpleasant surprises arising from its work.

When I first started using the keywords let and const , I basically applied let , resorting to const only when writing a new value to a variable declared with let could harm the program. But, learning more about programming, I changed my mind on this issue. Now my main tool is const , and let me use let only when the value of a variable needs to be rewritten. It makes me think about whether it is really necessary to change the value of some variable. In most cases, this is not necessary.

Do we need the var keyword?


The keywords let and const promote a more responsible approach to programming. Are there any situations in which the var keyword is still needed? Yes, there are. There are several situations in which this keyword is still useful to us. Think about what we are going to talk about before changing var to let or const .

▍var level of keyword support by browsers


Variables declared using the var keyword have one very important feature missing from let and const . Namely, we are talking about the fact that this keyword is supported by absolutely all browsers. Although the let and const browsers support is very good, however, there is a risk that your program will get into a browser that does not support them. In order to understand the consequences of such an incident, you need to consider how browsers treat unsupported JavaScript code, as opposed to, for example, how they react to incomprehensible CSS code.

If the browser does not support any CSS feature, then this basically leads to some distortion of what will be displayed on the screen. A site in a browser that does not support any of the styles used by the site will not look as expected, but it is very likely you can use it. If you use, for example, let , and the browser does not support this keyword, then your JS code will simply not work there. It will not be - that's all. Considering the fact that JavaScript is one of the important components of the modern web, this can be a very serious problem if you need your programs to work in outdated browsers.

When they talk about the support of sites by browsers, they usually ask themselves in which browser the site will work optimally. If we are talking about a site whose functionality is based on using let and const , then a similar question will have to be put differently: “In which browsers will our site not work?”. And this is much more serious than talking about whether or not to use display: flex . For most websites, the number of users with outdated browsers will not be large enough to be worth worrying about. However, if we are talking about something like an online store, or sites whose owners buy advertising, this can be a very important consideration. Before using new opportunities in such projects, assess the level of risk.

If you need to support really old browsers, but you want to use let , const and other new features of ES6 while doing this, one of the solutions to this problem is to use a JavaScript transpiler like Babel . Transporters provide a translation of the new code into what will be clear to the old browsers. Using Babel, you can write modern code that uses the latest features of the language, and then convert it to code that older browsers can perform.

, ? , . , , , , . , . , , . ES6-, Babel, Babel , , . , , . . ? - IE8 ? , , , , , .

▍ var


, var , . . :

 var myVar = 1; function myFunction() { var myVar = 2; //  ,     myVar    ! } 

, myVar , , . , . , , , , . , . var .

 var myVar = 1; function myFunction() { var myVar = 2; console.log(myVar); // 2 console.log(window.myVar); // 1 } 

var , window . let const . , JS- , (, , ) , .

, . , . , , , :

 let myGlobalVars = {}; let myVar = 1; myGlobalVars.myVar = myVar; function myFunction() { let myVar = 2; console.log(myVar); // 2 console.log(myGlobalVars.myVar); // 1 } 

, , , , . , , var , , , , , .

Results


, ? ? :


let const , ECMAScript 6, ( ) - -. , , , . , - , «» «» , , let const , .

Dear readers! , const var , let , , , ?

Source: https://habr.com/ru/post/420359/


All Articles