Function scope and block scope

Javascript has function-based scopes. Prior to ES6, block scopes were not created by for, if, and other statements, except for a few mechanisms that could create block scopes. We’ll talk about each of these cases separately. ## Function scope

function foo() {
    var a= 5;
    console.log(a);
    function bar() { var b = 3; console.log(b); } } foo(); / / 5, 3 bar (); // ReferenceError console.log(b); // ReferenceError console.log(a); // ReferenceErrorCopy the code

As shown in the previous example, two identifiers, a and bar, are created in function foo(). These identifiers cannot be accessed in the external scope, so references to both of them fail in the global scope because foo has only one identifier in the global scope. So the meaning of a function scope is that all variables belonging to the function can be used and reused throughout the function scope. The existence of a functional scope has two positive implications

  • Hiding an internal implementation hides a block of code by wrapping it inside a function, which satisfies the principle of minimum exposure. This principle states that in software design, you should expose as little as necessary and “hide” everything else.

  • Conflict avoidance Variables and functions in scope are “hidden” to avoid conflicts between identifiers of the same name (overrides or false calls between identifiers of the same name). Because block scopes are supported by many major languages, you may encounter some confusion when working with JavaScript.

for(var i = 0; i < 5; i++) { console.log(i); } console.log(i); / / 5if(1) { var j = 5; } console.log(j); / / 5Copy the code

JavaScript functions and variables declared in the for and if blocks belong to the outer scope, which means that the for and if statements do not create the block scope. But that doesn’t mean that block scopes don’t exist in JavaScript. There are still several ways to create block scopes in JavaScript.

with

The with statement extends the chain of scopes. Scopes created with the with object only apply to the with declaration, not the outer scope. Because with modifies or creates new scopes while the program is running, which can cause bugs and performance losses, it is not recommended or discussed here.

try/catch

The catch clause of the TRY /catch clause in the ES3 specification creates a block scope in which declared variables are only valid inside the catch.

try{
    undefined();
}
catch(err) {
    console.log(err);
}
console.log(err); // ReferenceError: err not found
Copy the code

let

One of the many exciting new features introduced in ES6 is the keyword let, which is a new way to declare variables. Let allows you to declare a variable, statement, or expression whose scope is limited to the block level, whereas var can only declare variables that are global or function scoped.

if(1) {
    let a = 5;
    sonsole.log(a); // 5
}
console.log(a); // ReferenceError
Copy the code

A typical example of letting to its advantage is the for loop discussed earlier.

for(let i = 0; i < 5; i++) {
    console.log(i);
}
console.log(i); // ReferenceError  
Copy the code

Using let to create count variables in the for loop effectively avoids contaminating the global (outer) namespace.

const

ES6 also introduced the const keyword, which creates a constant, which is also a block-scoped variable, and any attempt to modify the value will result in an error.

if(1) {const a = 3.14; console.log(a); // 3.14a = 3.1415926; / / error! } the console. The log (a); // ReferenceErrorCopy the code

ascension

This problem involves the order in which the JavaScript code is executed.

a = 2;
var a;
console.log(a);
Copy the code

Intuitively you would think that the code was executed line by line, so you would think that the above code was reassigned and therefore the output would be undefined, when in fact the output would be 2.

console.log(a);
var a = 2;
Copy the code

After the lesson of the last example, you might think that this program would also output a 2, or that it would throw a ReferenceError, but the truth is that the program’s output is undefined. To address both of these questions, we’ll look at one action that JavaScript code takes at compile time: promotion. In the compile phase of JavaScript code, part of the job is to find all the declarations and associate them with the appropriate scope. As a result, all declarations, including variables and functions, are processed first before any code is executed. When you see var a = 2; “, you might think of this as a single declaration, but JavaScript actually sees it as two declarations: var a; And a = 2; . The first definition declaration is made at compile time, while the second assignment declaration is left at execution time. Thus, the process is as if variable and function declarations have been “moved” to the top from where they appear in the code. This process is called ascension. This also explains why we can call the function before the code defined by the function.

foo(); / / 5function foo() {
    var a = 5;
    console.log(a);
}
Copy the code

One notable issue is that function declarations are enhanced. But function expressions are not promoted.

foo() // TypeError;
var foo = function bar() {
    console.log(5);
}
Copy the code

The foo variable in the above program is promoted and assigned to the current scope (global scope), so there is no ReferenceError when foo is called. But foo does not have an assignment at this point, so the function call operation on it raises TypeError.