"JS base" scope and closures

Source: Internet
Author: User

First, the compilation process common compiler language, before the execution of the program code will undergo three steps, called compilation. Step one: participle or lexical analysis breaks up a string of characters into meaningful blocks of code called lexical units. Example:
var a = 2;

This sentence is usually decomposed into the following lexical units: var, a, =, 2,; Step two: Parsing or syntactic parsing transforms a lexical unit stream (array) into a tree that represents the syntax structure of a program, composed of elements that are nested hierarchically.  This tree is called the abstract Syntax tree, AST example: var, a, =, 2,; A syntax tree similar to the following is generated:

Step Three: Code generation

Converts an abstract syntax tree (AST) into executable code.

However, for interpreted languages (such as JavaScript), the syntax tree is obtained through lexical analysis and parsing, and without the process of generating an executable, you can begin to interpret the execution.

for var a = 2; When processing, there are engines, compilers, and scopes involved.
Engine: Responsible for compiling and executing the entire Javascript program from beginning to end.
Compiler: Responsible for parsing and code generation.
Scope: Collects and maintains a series of queries consisting of all declared identifiers (variables), and implements a very strict set of rules that determine the access rights of the currently executing code to these identifiers (variables).

That's how they work together:
First the compiler will handle the following:
1, var A, the compiler will look in the scope to see if a variable that already has a name exists in the same scope collection. If so, the compiler will automatically ignore the declaration and continue compiling, otherwise it will require the scope to declare a new variable in the current scope's collection and name it a.
2. The compiler will then generate the code for the engine to run, which is used to handle the assignment of a = 2. The engine runtime first looks from the scope to see if a variable a exists in the current scope collection. If so, the engine will use this variable. If not, the engine will continue to look up the amount of change in the top-level scope collection.

Then if the engine finally finds the variable A, it assigns a value of 2 to it. If it is not found, an exception is thrown.

Summarize:
The assignment of a variable is done in two steps: The first step is to declare a variable in scope by the compiler (provided that it was not previously declared), and the second step is to find the variable in the scope at run time and assign a value to it if it can be found.


Second, scope
1, RL Query
In the previous section we said that the engine would look up the variable A. And the search is divided into two kinds, one is LHS (left-hand-side) query, and the other is RHS (right-hand-side) query.
LHS query: An attempt was made to find the container itself of a variable so that it could be assigned a value. That is, find the variable A.
RHS query: Finds the value of a variable. Finds the value of variable A, which is 2.
Example:

// here for A is a RHS query, find the value of a, and console.log out. 
// here A is a LHS query, find the variable A, and assign it a value of 2. 
function Foo (a) {    ///  2}foo (/////  here first calls to the Foo () function, executes the RHS query, finds the Foo function, and then executes the C8/>a = 2 of the parameter assignment, where the first execution of the LHS query finds A and assigns a value of 2, and then Console.log (a) executes the RHS query. 

Here's a bit more about nested nesting: when a block or function is nested within another block or function, the nesting that is used occurs. Traversal finds a nested scope by first looking for a variable from the current scope, and, if it is not found, continues looking like the previous level, and when the global scope is reached, the lookup ends, whether it is found or not. If the RHS lookup does not find the required variable in all nested scopes, the engine throws Referenceerror. If the desired variable is found, but you want to make an unreasonable operation, such as calling a value of a non-function type, the engine throws TypeError. If the LHS lookup does not find the required variable in the top-level global scope, if it is in non-strict mode, the global scope creates a variable with that name and returns it to the engine, and if it is in strict mode, the engine throws Referenceerror. Referenceerror and TypeError are common anomalies, and you need to know their differences to help you eliminate procedural problems. 2. The lexical scope is determined by where you write the variables and block scopes when you write the code. Example:
function Foo (a) {    var b = a*2;     function Bar (c) {        console.log (a,b,c);    }    Bar (b);} Foo (//  2,4,12

In this code there are three levels of scope, nested:

Scope 1: Contains the global scope, with the identifier: Foo. Scope 2: Contains the scope created by Foo, which has three identifiers: A, B, bar. Scope 3: Contains the scope created by bar, which has an identifier: C. When a variable is found, the scope lookup stops when it finds the first matching identifier. And it only looks for a first-level identifier, such as a, B, C, and for Foo.bar.baz, the lexical scope only looks for the Foo identifier, and after the variable is found, the object property access rules take over access to bar and Baz properties, respectively. One thing to say here is that the global variable automatically becomes the property of the global object, so it can be accessed indirectly through a reference to a global object property.

3. All declarations, including the promotion of variables and functions, are processed first before any code is executed. As an example: when you see var a = 2; , you might think of this as a declaration, but in fact Javascript sees it as two declarations: Var A; and a = 2, and executed at different stages. var A is carried out during the compilation phase, and A = 2 is left in place for the execution phase. This process is as if the variables and function declarations are "moved" from the position they appear in the code to the top, and this process is called variable elevation. Example:
foo (); function foo () {    console.log (a);     var a = 2;}

Learn the above knowledge, you should be able to guess Foo () can be executed normally, and Console.log (a) will play undefined; The reason is that when the promotion is applied to the above code, the code is equivalent to the following form:

function foo () {    var  A;    Console.log (a);     = 2;} Foo ();

There are two additional points of knowledge for variable elevation: 1, the function declaration is promoted, and the function expression is not promoted. The simplest way to differentiate between function declarations and function expressions is to see where the function keyword appears in the declaration. If the function declares the first word in the declaration, then it is a functional declaration, otherwise it is a function expression. Example: function declaration:
function foo () {    var  A;    Console.log (a);     = 2;}

function expression:

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

So for ascension, let's look at an example:

// Report typeerror Error var function () {    var  A;    Console.log (a);     = 2;}

This code is equivalent to

var Foo;foo ();   // at this point foo is undefined, and we try to make a function call to it, it is unreasonable operation, reported TypeError error.  function() {    var  A;    Console.log (a);     = 2;}

2, the function will be promoted first, then the variable. Example:
Foo ();  // 1           var foo; function foo () {    console.log (1function() {    Console.log (2);}

Will output 1 to not 2, which is equivalent to the following code promotion:

function foo () {    console.log (1function() {    Console.log (2);}

Note that var foo, although appearing before function foo (), is a duplicate declaration because the function declaration is promoted to the normal variable. Duplicate var declarations are ignored, but the function declarations that appear later overwrite the previous. Example:
// 3 function foo () {    console.log (1);} var function () {    Console.log (2)}function  foo () {    Console.log (3)} 

Closures are so-called closures: when a function can remember and access the lexical scope in which it is located, a closure is generated, even if the function is executed outside the current lexical scope. Example:
function foo () {    var a = 2;     function Bar () {        console.log (a);    }     return Bar;} var baz =//2

example, by calling Baz to invoke the bar inside Foo, bar is executed outside of its defined lexical scope, and after Foo executes, it is generally expected that the entire internal scope of Foo will be destroyed because the engine's garbage collector frees up memory space that is no longer in use. It seems that Foo is no longer being used, so it's natural to consider recycling it, but closures prevent it from happening, in fact the internal scope still exists and is not recycled because bar is still using the scope. Bar has a closure that covers the internal scope of Foo, allowing the scope to remain alive for the bar to be referenced at any later time. Bar still holds a reference to the scope, and this reference is called a closure. Bar is called outside the lexical scope of the definition, and closures allow the function to continue accessing the scope at the time of definition. In an example:
function Wait (message) {    setTimeout (function  timer () {        console.log (message)    },1000 )}wait ("HI");

A timer has a closure that covers the wait scope and retains a reference to the message. When wait executes 1s, its internal scope does not disappear, and the timer retains the wait-scoped closure, so you can get a message and Console.log, which is the closure. That is, if the function is used as the first-level value type and is passed around, you will see the closure applied to these functions. such as timers, event listeners, Ajax requests, cross-window communication, or any asynchronous task, as long as the callback function is used, the closure is actually used. Example:
function foo () {    var a = 2;     function Baz () {        //  2    }    Bar (baz);} function Bar (FN) {    // closures }

The intrinsic function Baz is passed to bar, and when you call Baz in bar, you can access the variable in the scope where he defined it, console.log out a. Example:
 for (var i=0;i<=5;i++) {    setTimeout (function  timer () {        console.log (i)    }, I*1000)}

We expect output several times 1-5, once per second, one at a time. The real output, however, is five 6. The reason is that the timer executes after the end of the loop, that is, I equals 6 o'clock, and even if you set the setTimeout time to 0, the callback function will be executed after the loop ends. So how do we fix it? We want the timer to capture a copy of I for Each loop, but according to the principle of the scope, the reality is that although the 5 functions of the loop are defined separately in each iteration, they are enclosed in a shared global scope, so there is actually only one I, and all functions share an I The reference. If the callback of the deferred function is defined 5 times, and the loop is not used, then it is exactly equivalent to this code. So the solution is that each iteration in the loop needs a closure scope. The immediate execution of the function can do just that.
 for (var i=0;i<=5;i++) {    (function(i) {        setTimeout (function timer () {            console.log (i)        },i*1000)    }) (i)}

Using an immediate execution function in a loop generates a new scope for each iteration, allowing the callback of the deferred function to enclose the new scope within each iteration, each of which will contain a correct I-waiting console. Problem solving.  Now you should really understand the scope and closure, find humorous do it, deepen the impression, or you will come back. Learn and thank the "You don't know JavaScript" roll up (fried chicken recommended for everyone to see)

"JS base" scope and closures

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.