preface

Recently, I reviewed the content of scope. I plan to summarize it and add my own understanding and attempts to better understand scope and improve related knowledge points.

Take a look at scopes

1. What is scope

As I understand it, in the execution of a program, values of variables need to be retrieved and stored, and in some cases these values need to be retrieved including states, so the set of values and states can be called scopes.

2. What are the scopes

From the point of view of behavior, scope can be divided into lexical scope and dynamic scope. We assume that JS will perform differently when using the two scopes respectively:

  • Lexical scope
var a = 3;
function test() {
    console.log(a); // Start from here
}
function test2() {
    var a = 1;
    test();
}
test2(); / / 3
Copy the code

Under lexical scope, the scope is static, determined by where the scope is declared.

So when test runs, it starts at the place where test is declared, and it looks for variable A, so it finds a value of 3, and we’ll talk about the rules for scoping.

  • Dynamic scope
var a = 3;
function test() {
    console.log(a);
}
function test2() {
    var a = 1;
    test(); // Start from here
}
test2(); / / 1
Copy the code

Under dynamic scope, the scope is dynamic, determined by the location of the specific call.

Under dynamic scope, the search starts at the location where test is executed, very similar to the this rule in JS.

Which scope does JS belong to? So, bam, bam, bam, clever, as you all know, JavaScript uses lexical scopes, so let’s go into JavaScript scopes in more detail.

Scope in JavaScript

As we mentioned above, JS uses lexical scope, so what types are they specifically divided into?

We can divide them into global scopes, function scopes, and block-level scopes (ES6).

1. Global scope

A global scope is created when it is created and destroyed when it is closed, and is the outermost or topmost layer of the scope. Code written directly in js files or script tags is globally scoped, in the same layer as window objects.

The global scope also has the following characteristics:

  • Declare a variable in a global scope. The variable automatically becomeswindowObject properties
  • Declare a function in global scope. The function automatically becomeswindowObject methods

Here’s a simple example:

var a=1;
function test(){
	console.log(2);
}

window.a;	/ / 1
window.test();  / / 2
Copy the code

2. Function scope

Declaring a function in JavaScript creates a collection of scopes belonging to the function itself. Variables declared in a function cannot be accessed from the outside, and the set of scopes is released when the function finishes executing.

Here’s a simple example:

function test(){
	var a=3;
}
console.log(a); //Uncaught ReferenceError: a is not defined
Copy the code

3. Block level scope

Many programming languages support the concept of block-level scope, which means that separate scopes are created when using if and for, but JavaScript syntax prior to ES6 does not have the concept of block-level scope, which can lead to situations like this:

var a=2;
if(a){
	var b=a;
}
b / / 2
Copy the code

The b variable declared in an if statement is accessible even outside the statement. But after ES6, with the let keyword, it looks like this:

var a=2;
if(a){
	let b=a;  //var becomes let
}
b //Uncaught ReferenceError: b is not defined
Copy the code

As you can see, with the let keyword,if statements have the same independent scope as functions.

Lookup rules for scopes

We might have been a little confused when we were talking about different scopes:

  • What do I mean by outside and inside of a scope?
  • Why can’t an external scope access an internal scope?
  • Why does the inside get the outside variable?

Here we are going to talk about the scope of the lookup rules, we do not talk more, first please show our example diagram:

When we execute this code, we will see that the final output is “I am the name of the outer scope”.

Let’s take a look at what happens in this process, starting with where name is printed:

function inner(){
	consoleThe (name); }Copy the code

As we said earlier, functions have independent scopes. Here we want to output name, need to get the value of name, so the JS engine will first go to the variable in the scope to find the corresponding value.

The scope of the name variable is the scope of the inner function, and the name variable is not defined.

This brings us to one feature of scope: scope nesting.

When a block or function is nested within another block or function, nesting of the scope is freed. Therefore, when a variable cannot be found in the current scope, the engine will continue searching in the outer nested scope until it finds the variable or reaches the outermost scope (that is, the global scope).

As you can see from our sample diagram, we have three nested scopes, and when the engine fails to find the name variable in inner’s scope, it continues to look for it in its outer scope.

When we get to the outer scope of the inner scope, we find a declared name value and I’m the name of the outer scope, so we’re happy to go back with that value and print it out. Note that since the value of the variable is already found in the inner scope, the value of the external variable with the same name is not accessed. This is called **” shadowing effect “**.

ascension

1. Variable promotion

A concept closely related to scope is promotion. Let’s start with a simple little example of what it looks like:

a=2;
var a;
Copy the code

In fact, this is due to precompilation of JS. This is a summary of what precompilation does:

  • Find all declarations and ascend to the top of their scope

So the code I just saw actually executed in this order:

var a;
a=2;
Copy the code

It is important to note that variables are declared ahead of time and assigned in the same order as before. For example:

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

This code outputs a as undefined, indicating that when console.log(a) is executed, a has already been declared, otherwise a ReferenceError will be raised. This also shows that the declaration of variables is indeed advanced, but the assignment is not. The code actually runs like this:

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

That’s a lot clearer.

2. Function promotion

In addition to the above example, there is also an improvement in the declaration of functions. Let’s look at an example:

f()	//test
function f(){
	console.log('test');
}
Copy the code

As you can see, the f() statement preceded the declaration, but finally successfully printed “test”, indicating that the function declaration was advanced. Note, however, that ** declaring a function through a function expression does not promote it. ** Let’s look at an example:

f()	//Uncaught TypeError: f is not a function
var f = function(){
	console.log('test');
}
Copy the code

In this example, the function f is declared as an expression, and an error is reported when f() is executed, indicating that no promotion has occurred. A function expression that declares a function can be thought of as a variable, so it makes sense:

var f
f()	//Uncaught TypeError: f is not a function
f = function(){
	console.log('test');
}
Copy the code

Because, as we say, variables are declared ahead of time and assignments are not, we have this result.

3. Variable promotion and function promotion

Given variable promotion and function promotion, let’s try, what happens when we try to declare functions and variables with the same name?

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

It can be seen that the final output of A is a function instead of undefined, which not only indicates that function promotion has a higher priority than variable promotion, but also indicates that the declaration and assignment of function is actually a whole process from the value of A is a function, rather than the promotion declaration without promotion assignment of variable promotion. So this is what this code looks like:

function a(){}
varA;console.log(a);  / / ƒ (a) {}
a=2;
Copy the code

So if you add another statement to the code that says “A”, you’ll see that the value of “A” will be overwritten by “2”.

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

Added to improve

Let’s take a look at a special case:

if(function f(){{})console.log(f); //Uncaught ReferenceError: f is not defined
}
Copy the code

Why declare the function f in the if statement, but output it as undefined? The reason is that when a function is declared in an expression, it is not treated as a function declaration, but is evaluated as an expression, and the evaluation does something like this:

Create a new context in which to declare the function and return the function object. If the returned object is not stored by the variable, the new context is invalidated, freeing the f function object.

The function declaration in if() here is not stored in variables, so when console.log(f) is executed, f has already been released, so an error is reported. Let’s try storing it in a variable:

if(f=function(){{})console.log(f);  / / ƒ () {}
}
Copy the code

Perfect finish!

conclusion

This article introduced the concept of scope and JavaScript scope and promotion rules under scope. It also saw several non-Sang, xin, Bing, fun, kuang examples

Write in the last

All see here, if feel helpful to you might as well click a “like” attention to support bai ~

More articles and knowledge points will be updated in the future. If you are interested, you can pay attention to a wave of ~

If there are any mistakes or inaccurate descriptions, we welcome you to point out and communicate ~