Scope (Scope)

1. What is scope

Scope is the accessibility of variables, functions, and objects in a specific part of the runtime code. In other words, a scope determines the visibility of variables and other resources in a block of code. Let’s look at an example:

Function outFun2() {var inVariable = "inVariable "; } outFun2(); // Execute this function first, otherwise you don't know what's inside console.log(inVariable); // Uncaught ReferenceError: inVariable is not definedCopy the code

As you can see from the example above, the inVariable variable is not declared in the global scope and therefore the value of its inVariable is not specified in the global scope. We can think of it this way: the scope is an independent site, so that variables do not leak out, exposed. In other words, the greatest use of a scope is to isolate variables. Variables of the same name in different scopes do not conflict.

Before ES6, JavaScript had no block-level scope, only global scope and function scope. With the advent of ES6, we have ‘block-level scope’, which can be embodied by the addition of the let and const commands.

2. Global scope and function scope

Objects that can be accessed anywhere in the code have a global scope. In general, the following situations have a global scope:

  • Outermost functions and variables defined outside the outermost function have global scope
Var outVariable = "I am the outer variable "; Function inVariable () {var inVariable = "inVariable "; Function innerFun() {// Console. log(inVariable); } innerFun(); } console.log(outVariable); // I am the outermost variable outFun(); // The inner variable console.log(inVariable); //inVariable is not defined innerFun(); //innerFun is not definedCopy the code
  • All variables that do not define a direct assignment are automatically declared to have a global scope
Function outFun2() {variable = "undefined "; Var inVariable2 = "inner variable 2"; } outFun2(); Console. log(variable); console.log(variable); // Not defining the directly assigned variable console.log(inVariable2); //inVariable2 is not definedCopy the code
  • All properties of window objects have global scope

In general, the built-in properties of a window object have a global scope, such as window.name, window.location, window.top, and so on.

The downside of global scope is that if we write many lines of JS code and the variable definitions are not included in functions, then they are all in the global scope. This will pollute the global namespace and cause naming conflicts.

Var data = {a: 100} var data = {x: true}Copy the code

That’s why jQuery, Zepto, etc., all the code is placed in (function(){…. }) (). Because all variables placed inside will not be leaked and exposed, will not pollute the outside, will not affect other libraries or JS scripts. This is a representation of the scope of the function.

A function scope is a variable declared inside a function. In contrast to a global scope, a local scope is generally accessible only within a fixed code fragment, most commonly within a function.

Function doSomething(){var blogName=" doSomething "; function innerSay(){ alert(blogName); } innerSay(); } alert(blogName); // Script error innerSay(); // Script errorCopy the code

Scopes are layered, with inner scopes having access to variables in outer scopes and not vice versa. Let’s look at an example where the scope analogy is easier to understand by using bubbles:

The final output is 2, 4, 12

  • Bubble 1 is global scoped and has identifier foo;
  • Bubble 2 is scoped foo and has identifiers A,bar,b;
  • Bubble 3 is scoped bar and has only identifier C.

It is worth noting that block statements (the statements between braces “{}”), such as if and switch conditional statements or for and while loop statements, unlike functions, do not create a new scope. Variables defined in block statements will remain in the scope where they already exist.

If (true) {// The 'if' conditional statement block does not create a new scope var name = 'Hammad'; // name is still in global scope} console.log(name); // logs 'Hammad'Copy the code

JS beginners often take a while to get used to variable promotion, and not understanding this particular behavior can lead to bugs. Because of this, ES6 introduces block-level scopes to make the life cycle of variables more manageable.

3. Block level scope

Block-level scopes can be declared with the new let and const commands. The declared variables cannot be accessed outside the scope of the specified block. Block-level scopes are created when:

  1. Inside a function
  2. Inside a code block (enclosed by a pair of curly braces)

The syntax of the let declaration is the same as that of var. You can basically use let instead of var to declare variables, but limit the scope of variables to the current code block. Block-level scopes have the following characteristics:

  • Declared variables are not promoted to the top of the code block

The let/const declaration is not promoted to the top of the current code block, so you need to manually place the let/const declaration at the top to make variables available within the entire code block.

function getValue(condition) { if (condition) { let value = "blue"; return value; } else {// value cannot return null here; } // value is not available here}Copy the code
  • No duplicate declaration

If an identifier is already defined within a code block, let declarations using the same identifier within that code block will result in an error being thrown. Such as:

var count = 30;
let count = 40; // Uncaught SyntaxError: Identifier 'count' has already been declared
Copy the code

In this case, the count variable is declared twice: once using var and once using let. Because let cannot repeatedly declare an existing identifier in the same scope, the let declaration here throws an error. However, if you declare a new variable with the same name using let in a nested scope, no error is thrown.

var count = 30; If (condition) {let count = 40; // Other code}Copy the code
  • Nifty use of bound block scope in loops

Developers probably want to implement block-level scope for the for loop the most, because you can restrict declared counter variables to the loop. For example, the following code is often seen in JS:

<button> test 1</button> </button> </button> </button> test 3</button> <script type="text/javascript"> var BTNS = document.getElementsByTagName('button') for (var i = 0; i < btns.length; I++) {BTNS [I] onclick = function () {the console. The log (' first '+ (I + 1) +' a ')}} < / script >Copy the code

We want to implement a requirement that when we click on a button, it says “the NTH button was clicked”. We don’t think about the event broker here, but when we click on any button, we will see “the fourth button” in the background, because I is a global variable, and when we click on the event, the value of I is 3. So how do I change that, the simplest way is to declare I with let

for (let i = 0; i < btns.length; I++) {BTNS [I] onclick = function () {the console. The log (' first '+ (I + 1) +' a ')}}Copy the code

The scope chain

1. What are free variables

Let’s start by recognizing what a free variable is. In the following code, console.log(a) gets variable A, but a is not defined in the current scope (see b). Variables that are not currently defined in scope become free variables. How to get the value of the free variable – look for the parent scope (note: this is not strictly stated, but will be explained more carefully below).

Var a = 100 function fn() {var b = 200 console.log(a)} fn() {var b = 200 console.log(b)} fn()Copy the code

2. What is scope chain

What if the parent doesn’t have either? Search up and down until you find the global scope. If you don’t find it, you give up. This layer by layer relationship is the scope chain.

Var a = 100 function F1() {var b = 200 function F2() {var c = 300 console.log(a) Console.log (b) // free variable, console.log(c) // local variable} F2()} F1()Copy the code

3. The value of free variables

As for the value of a free variable, it is mentioned above that it should be taken from the parent scope. In fact, sometimes this interpretation will produce ambiguity.

Var x = 10 function fn() {console.log(x)} function show(f) {var x = 20 (function() {f() //10, not 20})()} show(fn)Copy the code

In f sub n, what scope do I go to when I take the value of the free variable x? — to the scope in which the fn function was created, regardless of where the fn function will be called.

So don’t use that anymore. Instead, it would be more appropriate to say “go to the field where the function was created”. Scoped values, this is “create”, not “call”, remember — this is actually called “static scope”.

var a = 10
function fn() {
  var b = 20
  function bar() {
    console.log(a + b) //30
  }
  return bar
}
var x = fn(),
  b = 200
x() //bar()
Copy the code

Fn () returns the function bar, which is assigned to x. By executing x(), the bar function code is executed. When taking the value of b, fetch it directly from fn’s scope. The value of a is 30, and the value of a is 30. The value of a is 30, and the value of a is 30

Scope and execution context

Many developers often confuse the concepts of scope and execution context, thinking they are the same, but they are not.

We know that JavaScript is an interpreted language, and the execution of JavaScript is divided into two stages: interpret and execute, which do different things:

Interpretation stage:

  • Lexical analysis
  • Syntax analysis
  • Scope rules are determined

Execution stage:

  • Create execution context
  • Execute function code
  • The garbage collection

The JavaScript interpretation phase determines the scope rule, so the scope is defined when the function is defined, not when the function is called, but the execution context is created before the function executes. The most obvious aspect of the execution context is that the reference to this is determined at execution time. The variables accessed by scope are determined by the structure of the code that was written.

The biggest difference between scope and execution context is that the execution context is determined at run time and can change at any time; The scope is defined at definition and does not change.

A scope may contain several contexts. It is possible that there is never a context (the function is never called); It may have, but now the context is destroyed after the function is called; It is possible to have one or more at the same time. In the same scope, different calls will have different execution contexts, resulting in different variable values.

Exercises:

(1)

var a = 1 function fn1(){ function fn2(){ console.log(a) } function fn3(){ var a = 4 fn2() } var a = 2 return fn3 } var Fn = fn1() fn() // output a=2 // execute fn2, fn2 cannot find variable A, and then find a=2 in the scope fn1 where the current fn2 was createdCopy the code

(2).

var a = 1 function fn1(){ function fn3(){ var a = 4 fn2() } var a = 2 return fn3 } function fn2(){ console.log(a) } var Fn = fn1() fn() // output how much // output a=1 // finally execute fn2, fn2 cannot find variable A, and then find a=1 in the global scope where the current fn2 was createdCopy the code

(3) 【 key 】

var a = 1 function fn1(){ function fn3(){ function fn2(){ console.log(a) } var a fn2() a = 4 } var a = 2 return fn3 } Var fn = fn1() var fn = fn1() var fn = fn1() var fn = fn1() var fn = fn1() var fn = fn1() var fn = fn1()Copy the code

In fact, I’ve found that the function expressions that execute immediately in the previous section also give you a sense of the difference between local and global scope in nature.

(4) further look at a few classic questions on the Internet, feel the IQ has been fuzzy ~ (in fact, it is also ok, seriously or there is no impact on IQ) in the scope chain to find the process of pseudo-code

1 problem

Var x = 10 function foo() {console.log(x)} function bar(){var x = 30 foo()} /* The bar function calls foo on line 3, and foo looks for x from its local environment and doesn't find it on line 1. Foo looks for x from its global environment and finds var x=10. The output of foo() is 10. * /Copy the code

The second problem is

var x = 10; bar() //30 function bar(){ var x = 30; function foo(){ console.log(x) } foo(); } /* On line 2, bar() calls function foo on line 3, and foo on line 4 looks for x in its local environment and doesn't find it. Foo looks for x in its local environment, bar, and finds var x = 30 so bar() on line 2 is 30 */Copy the code

The third problem is

var x = 10; bar() function bar(){ var x = 30; Function (){console.log(x)})(function() {console.log(x)})(function() {console.log(x)})(function() {console.log(x)})(function() {console.log(x)}) Var x =30; function() =30; function() =30Copy the code