Scope

In JavaScript, what is a scope (Scope)?

Let’s look at the following code:

(function(){
    var text = "Hello,world!"; }) ();console.log(text);
Copy the code

When we try to access immediately execute the function((function(){…. })())(ReferenceError: text is not defined.

As you can see, variables inside a function cannot be accessed from outside the function, and the accessibility of variables is called scope.

There are currently three types of scope in JavaScript:

  1. Global scope
  2. Function scope
  3. Block-level scope

As the name implies, a global scope applies to all code, and objects in a global scope can be accessed anywhere.

Variables have global scope in the following cases:

One case is: variables defined in the outermost function

var text = "Hello,world!"
function print(){
    console.log(text);
}
print();
console.log(text);
Copy the code

Output:

Hello,world!
Hello,world!
Copy the code

Also, the text variable automatically becomes a member of the Window object.

<script>
    var text = "Hello,world!"
    console.log(window.text);
</script>
Copy the code

Output:

Hello,world!
Copy the code

And, allwindowProperties of objects have global scope.

There are also cases where variables that are not defined but assigned automatically have global scope:

<script>
    (function(){
        text = "Hello,world!";Var text = "Hello,world!" ;}) ();console.log(text);
    console.log(window.text);
</script>
Copy the code

Output:

Hello,world!
Hello,world!
Copy the code

The scope of a function is the scope that is defined within a function. The example at the beginning of this article is an error caused by an external function accessing an internal function variable.

(function(){
    var text = "Hello,world!";
    (function(){
        console.log(text);
        text = "Hi!";
        console.log(text); }) (); }) ();Copy the code

Output:

Hello,world!
Hi!
Copy the code

Try external access:

(function(){
    var text = "Hello,world!"; }) ();console.log(text);//ReferenceError: text is not defined
Copy the code

Scopes are nested like nesting dolls. At the same time, an inner scope can access objects in an outer scope, but not vice versa.

It is worth noting that JavaScript, unlike other languages, does not have block-level scope for variables defined with var.

// The text variable in if can be accessed externally
if(true)
{
    var text = "Hello,world!";
}
console.log(text);
Copy the code

Output:

Hello,world!
Copy the code

Obviously, without block-level scope it is easy to create variable naming conflicts. Fortunately, after E6, JavaScript provides the let, const keyword to give variables block-level scope.

// Replace var with let
if(true)
{
    let text = "Hello,world!";
}
console.log(text);
Copy the code

ReferenceError: text is not defined

  1. Unlike var declarations, variables declared by let/const do not get promoted:
if(true)
{
    console.log(text);
    let text = "Hello,world!";
}
Copy the code

ReferenceError: Cannot access 'text' before initialization

If you need to make variables declared by let/const accessible throughout the block-level scope, the easiest way to do this is to move the variable declaration code to the top of the block-level scope. I’m not going to do that anymore.

  1. Duplicate declarations are not allowed:
let text = "Hello,world!";
var text = "Hello,world!";//let text = "Hello,world!" ; In the same way
Copy the code

SyntaxError: Identifier 'text' has already been declared

But it allows repeated declarations in different scopes:

var text = "Hello,world!";//let text = "Hello,world!" ; In the same way
if(true) {let text = "Hello,world!";
}
console.log(text);
Copy the code

Output:

Hello,world!
Copy the code

Guess the output of the following code:

var events = new Array(a);for(var i = 0; i <3; i++){ events[i] =function(){console.log(i)};
}
events.forEach(e= > {
    e();
});
Copy the code

0,1,2 or 3,3,3?

After careful analysis, each method of Events outputs variable I, and there is no variable I in the method. Following the principle of scope, we searched for variable I upward and found cyclic variable I, and the final value of I was 3, so the output result should be: 3, 3, 3.

That doesn’t seem like what we want. How do we get to 0,1,2? We can wrap it in a function that executes immediately:

var events = new Array(a);for(var i = 0; i <3; i++){ (function(n){
        events[i] = function(){console.log(n)};
    })(i);
}
events.forEach(e= > {
    e();
});
Copy the code

Since each variable I is independent, the final output is: 0,1,2.

A faster way is to simply replace var with let.

Why is this possible? Even if let I is block-scoped, it is still 3 when I is accessed by scope rules.

The JavaScript engine internally remembers the value of the previous cycle, and when it initializes the variable I of this cycle, it evaluates on the basis of the previous cycle.

We can assume that the loop variables declared by let are independently scoped by JavaScript at a block level each time, so each I is in a different scope, resulting in different output results.

The simulated code for the part of the for loop can be seen as:

/ /...
let i = 0;
if(i < 3) {let k = i;
    events[i] = function(){console.log(k)};
}
i++;
if(i < 3) {let k = i;
    events[i] = function(){console.log(k)};
}
i++;
if(i < 3) {let k = i;
    events[i] = function(){console.log(k)};
}
i++;
/ /...
Copy the code

Scope chain

Objects that are scoped outside can be accessed from within an inner scope.

Variables that are not internally scoped but can be accessed are called free variables.

var text = "Hello,world!"
function print(){
    console.log(text);// Text is the free variable
}
print();
Copy the code

In the code above, the text variable is not defined in the print method, so when we access it, we look for text one level up in the scope and find it.

Scope chains represent hierarchical relationships between scopes.

The value of the free variable is worth considering:

var text = "Hello,world!"
function print(){
    console.log(text);
}
function test(){
    var text = "Hi!";
    print();
}
test();
Copy the code

If the value of the free variable is simply to look for the variable in the scope of the code one level up, the output will be: Hi! . But here his output is:

Hello,world!

So, the value of a free variable needs to find the scope where the function was created and then look up, not the scope where the function was called.

var i = 10;
function sum(){
    var j = -10;
    return (function(){
        i += j;
    });
}
var j = 10;
sum()();
console.log(i);
Copy the code

0 or 20?

Call sum()() > I += j > variable j is a free variable > find > var j = -10 from the level above which the function was created (sum), so the result is 0.