What is scope

A scope is the accessibility of variables, functions, and objects in specific parts of the code run time. In other words, a scope determines the visibility of variables and other resources in a code block

There are two working models for scopes

  • Lexical scope (static scope)
  • Dynamic scope

Lexical scope

Lexical scope, also known as static scope, is the scope defined at the lexical stage. In other words, the lexical scope is determined by where you write the variable and block scope when you write the code. The JS scope uses lexical scope

Here’s an example:

var a = 2
function foo() {
   var b = 2 
   console.log(a + b)
}
foo() / / 4
Copy the code

Function foo defines what variables it can get, so if foo has a in it, it takes it directly, otherwise it goes up one level.

Example 2:

var a = 2
function bar() {
    console.log(a)
}
function foo() {
    var a = 3
    bar()
}
foo() / / 2
Copy the code

Bar will only look up from its defined position, that is, find a to be 2

Dynamic scope

The scope of a function is determined at the time the function is called, and bash is dynamically scoped

Save the following code as shell to execute the final output is: 3

#! /bin/bash
a=2
foo() {echo $a
}
bar(){
    a=3
    foo
}
bar
Copy the code

Scope in Javascript

There are two types of scope in JavaScript

  • Global scope
  • Local scope

Global scope

The entire JavaScript document is a global function, and variables are defined outside of functions, so variables are globally scoped

var a = 1
Copy the code

You can access and change in any other scope

var a = 1

console.log(a) / / 1

function bar() {
    console.log(a)
}

bar() / / 1
Copy the code

Local scope/function scope

Variables defined within a function are in the local scope. Variables are bound to functions. Each function has a different scope and is not accessible from other functions

// Global scope
function foo() {
    // local scope
   var a = 1
   console.log(a) / / 1
}
// Global scope
function bar () {
    // local scope
   console.log(a) // Uncaught ReferenceError: a is not defined
}
Copy the code

Scope subdivision

Nested scopes

Scope nesting occurs when a block or function is nested within another or function, so that a variable cannot be found in the current scope and the engine will continue searching in the outer nested scope until it finds that variable or reaches the outermost scope

function foo(a) {
    console.log(a + b)
}
var b = 2
foo(2) / / 4
Copy the code

In foo, if b is undefined, it looks up one level in scope and stops looking.

Scoped look-ups stop when the first matching identifier is found. Multiple layers of nested scopes can define identifiers of the same name. This is called the “masking effect” (internal identifiers “mask” external identifiers)

Ex. :

var a = 2
function foo() {
    var a = 3
    function bar () {
       console.log(a)
    }
    return bar;
}
foo()() / / 3
Copy the code

Block scope

Unlike functions, statements like if, switch, while, and for do not create new scopes, but block scopes. If you use the VAR definition, you end up in an external scope

if (true) {var a = 1
}
if (false) {
    var b = 2
}
console.log(a) / / 1
console.log(b) // Undefined is in the block scope. When using var declarations, where does it end up in the outer scope

for (var i = 0; i <10; i++){ }console.log(i) / / 10
Copy the code

Use try catch, with statements to create block scopes

try {
    undefined(a)// Throw an error
} catch (err) {
    console.log(err) // Normal execution
}
console.log(err) // Uncaught ReferenceError: err is not defined
Copy the code

The let and const keywords in ES6 support declaring local scopes within block statements

if (true) {var a = 1
    let b = 2
    const c = 3
}
console.log(a) / / 1
console.log(b) // Uncaught ReferenceError: b is not defined
console.log(c) // Uncaught ReferenceError: c is not defined
Copy the code

You can also create a display

if (true) {
    var a = 1
    {
        let b = 2
        const c = 3}}console.log(a) / / 1
console.log(b) // Uncaught ReferenceError: b is not defined
console.log(c) // Uncaught ReferenceError: c is not defined
Copy the code

Use block scope for garbage collection

function process(data) {}var someReallyBigData = { ... }
process(someReallyBigData)
var btn = document.getElementById("my_button")
btn.addEventListener("click".function click(evt) {
    console.log("button clicked")})Copy the code

The click callback of the click function does not require the someReallyBigData variable. Be fooled by the process of theory After execution, data structures that take up a lot of space in memory can be reclaimed. However, because the click function forms a closure that covers the entire scope, the JavaScript engine is most likely still storing this structure

Block scope solves this problem

function process(data) {} {let someReallyBigData = { ... }
    process(someReallyBigData)
}

var btn = document.getElementById("my_button")
btn.addEventListener("click".function click(evt) {
    console.log("button clicked")})Copy the code

ascension

Declarations are moved from where they appear in the code to the top, a process called variable promotion

Promotion of variables

Look at the following code

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

The reason for this is that the engine compiles the Javascript code before it interprets it, and part of the compilation phase is finding all the declarations and associating them with the appropriate scope

When the compiler sees var a = 2, it sees two declarations var a; And a = 2; The first definition declaration is made at compile time. The second assignment declaration is left in place for execution so the above code is processed as follows

var a;
a = 2;
console.log(a)
Copy the code
var a;
console.log(a)
a = 2 // Stay where you are
Copy the code

The whole process above is called promotion, and in general there is declaration before assignment

Note: Only the declaration itself is promoted; assignment or other running logic is left in place

Enhancement of function

Look at the following code

foo()

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

The declaration of the foo function is enhanced because the first line can be called and executed normally, and the above code can be interpreted as follows

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

foo()
Copy the code

Note that function expressions are not promoted

foo() // Uncaught TypeError: foo is not a function
var foo = function bar() {}Copy the code

The above code can be interpreted in the following form

var foo;
foo()
foo = function() {
    varbar = ... self... }Copy the code

Function is preferred

Both function declarations and variable declarations are enhanced. Functions are promoted first, then variables.

Ex. :

foo() / / 1
var foo; // Declare variables
function foo() {
    console.log(1)}// Declare the function

foo = function() {
    cosnole.log(2)}Copy the code

The result will print 1. If the variable is promoted first, TypeError will appear.

function foo() {
    console.log(1)
}

foo() / / 1

foo = function() {
   console.log(2)}Copy the code

Var foo though appears in function foo()… Before the declaration, but it is a duplicate declaration and is directly ignored. Function declarations are promoted before ordinary variables.

Note: The following function declaration can override the previous example:

foo(); / / 3

function foo() {
    console.log(1)}var foo = function() {
    console.log(2)}function foo() {
    console.log(3)}Copy the code

Try to avoid writing this code

Execution environment (Execution context)

The execution environment (execution context) defines other data that variables or functions have access to and determines their respective behavior. Each execution environment has a variable object associated with it, and all variables and functions defined in the environment are stored in this object

Global execution environment

The global execution environment is the Window object in a WEB browser, so all global variables and functions are created as properties and methods of the window. After all code in an execution environment is executed, the environment is destroyed, along with all variables and function definitions saved in it. (The global environment is not destroyed until the application exits – the web page or browser is closed

Function execution environment

Every function has an execution environment, and when the execution stream enters a function, the environment of the function is pushed into an environment stack. After the function executes, the stack ejects its environment, and control returns to the previous execution environment, with the browser always executing the execution environment at the top of the stack

The execution environment has two phases

1. Creation phase

When a function is called but its code has not yet been executed

  • Create a variable (activity) object
  • Create scope chains
  • Set the context, that isthis

The variable object

Also known as an active object, it contains all variables, functions, and other declarations defined in the execution environment. When a function is called, the interpreter scans all of its resources including function arguments, variables, and other declarations. The initial value of the variable object contains the arguments object

'variableObject': {}Copy the code

The scope chain

The scope chain is created after the variable object. The scope chain itself contains variable objects. A scope chain is an ordered access to all variables and functions that the execution environment has access to

'scopeChain': {}Copy the code

Abstract the execution environment into an object:

executionContextObject = {
    'variableObject': {},
    'scopeChain': {},
    'this': {}}Copy the code

Ex. :

function foo () {
    var a = 1;
    function bar () {
        var b = 1;
    }
    bar()
}
Copy the code

Foo ()’s active object at creation time is: arguments,a,bar Foo () ‘s scope chain is: Foo > window foo() this is the execution environment for window foo() [Img-AHY5xUNQ-1639723468249] (EvernotecID :// 328b33FE-ED00-4b09-8BC9-557510243583 /appyin xiangcom/33490301/ENResource/p120)]

2. Implementation phase

In the execution phase, the values in the variable object are assigned and the code is finally executed

Scope chains and free variables

scope

The nested relationships of scopes form a scope chain, which is created for variable objects when code is executed in the execution environment.

Ex. :


function foo () {
    var a = 1
    function bar() {
        var a = 2}}Copy the code

In this example, the scope chain of the bar function is bar>foo>window(global) and the scope chain of the foo function is foo>window(global)

The scope chain is mainly used to query identifiers (variables and functions). Identifier (variables and functions) parsing is the process of searching identifiers level by level along the scope chain, while the scope chain is to ensure orderly access to variables and functions

Free variables

What is a free variable

A variable that exists in, but is not declared in, the current scope

Ex. :

var b = 2; 
function foo() {
    var a = 1
    function bar() {
        var c =  b + a
    }
}
Copy the code

In the above example, B and A are not declared in scope bar, so A and B are free variables. Once a free variable appears, there will definitely be scope chain, and then the corresponding variable can be found according to the scope chain search mechanism.

conclusion

Scopes and scope chains are a very important part of the foundation of Javacript, and understanding them will help you learn about closures.

Reference documentation

Understanding Scope in JavaScrip


Chiba’s blog