javascript--Scopes and closures

Source: Internet
Author: User
Tags function definition

--Excerpt from "You Don T Know js-scope, Closures"

For all programming languages, scopes are a fundamental concept. An in-depth understanding of the scope in JavaScript plays an important role in the correct use of the language.

What is a scope

Scopes are rules for how a set of variables are stored and read, and there are two types of models:

    1. Static scopes (also known as literal scopes, lexical scopes).
    2. Dynamic scopes.
Operation of the scope

There are two types of operations on scopes: read operations, write operations.

The number of operands read in the compilation principle is called the right operand (RHS), and the operand modified is called the operand (LHS).

This name comes from an assignment expression: a = B;

A in the left is lhs,b on the right for RHS.

Static scope

Variables in JavaScript are scoped to a static scope.

Static scopes are determined when the code is written, and can be nested.

As shown below, there are three scopes in the code snippet, and there is a strict nesting relationship between scopes:

Scope 3 is a subset of scope 2, and scope 2 is a subset of scope 1.

function Foo (a) { var b = A * 2; function Bar (c) {Console.log (A, B, c);} bar (b * 3);} Foo ( 2); // 2 4


Finding a variable

The JavaScript execution engine starts the query operand from the current scope (LHS,RHS) and, if the operand is not in the current scope, performs a lookup up to the global scope.

If the global scope still does not find the operand, the query fails.

So the output of the example code is 2 4 12.

Behavior of operand query failure

RHS query failed, throw Referenceerror exception.

LHS query fails, a variable with the same name is created in the global scope. (in strict mode, the Referenceerror exception is thrown).

Hack scope

The Hack scope is not a recommended behavior, but it can help in-depth understanding of the scope of JavaScript. You can modify the scope of the execution code with the eval or with keyword.

Eval modifies a scope that already exists

The code in eval runs in the current scope and modifies or accesses the number of operands that can be queried in the current scope.

As shown below:

function foo (str, a) {    //  cheating!     var b = 2//1, 3     

"var B = 3;" Runs in the scope of function foo () and declares variable B in this scope.

The variable B in the function foo () scope overrides B in the global scope, so the program output is 1, 3.

In strict mode, the code in eval runs in the new scope, which is a subset of the current scope.

As shown below:

function foo (str) {    "use strict";    eval (str);     // referenceerror:a is not defined  "var a = 2");

"var a = 2;" Run in a separate scope, this scope is a subset of the function foo () scope, so Console.log (a) throws a Referenceerror exception when querying right operand a.

With Create a new scope

The WITH keyword creates a separate scope for the object, which is a subset of the current scope.

function foo (obj) {     with (obj) {        = 2;     var o1 = {    3var o2 = {    3///  //  undefined//  2--Oops, leaked global!  

Foo (O1) looks for operand A in the O1 scope and modifies its value, so the Console.log (o1.a) output is 2.

Foo (O2) found in the O2 scope to do operand A, the query failed. Continuing to find in the parent scope function foo () also fails. The global scope also does not have the definition of exercises left number A.

Following the left operand query rule, the JavaScript execution engine creates a new variable A in the global scope and assigns a value of 2, and a scope leak occurs.

function scope

Before ES3, a function is the only way to create a scope. Each function creates a scope and is nested within the current scope.

The operands defined in the function scope can be used anywhere in this function or in its child scope, but not in the ancestor scope.

The function scope can therefore be used as a namespace to prevent multiple module naming collisions.

As shown below:

function DoSomething (a) {    function  dosomethingelse (a) {        return A-1;         var= a + Dosomethingelse (A * 2 );      * 3//  

function Dosomethingelse () is accessible only in the functions dosomething () scope, which plays the role of encapsulation.

ES3 before the function is the smallest unit of scope. The operands that are created anywhere in the function are scoped to this function.

As shown below:

function foo () {    function  Bar (a) {        //  changing the ' I ' in the enclosing scope ' s fo R-loop        Console.log (A + i);          for (var i=0; i<10; i++) {        //  oops, infinite loop ahead!     }}

This code will fall into a dead loop. Although "var i = 0" is declared in a for loop, because the function is the smallest unit of scope, I is scoped to function foo ().

function bar () modifies I to 3, leading to the occurrence of a dead loop.

function expression

A function creates a scope that encapsulates its interior and prevents naming conflicts. However, the name of the function is the main source of the naming conflict.

The function expression resolves the problem of a function name conflict.

How to distinguish function definitions from function expressions

The entire statement begins with the function keyword, which is defined by the functions.

Conversely, the function is identified as an expression of functions.

As shown below:

function // function Definition (function// functions Expression

Scope of function names in function expressions

The function expression creates a new scope, which is a subset of the current scope.

With (function foo () {...}) For example, the function name Foo in this function expression can be accessed only in the location shown in "...".

Block scope

ES3 specifies that the try/catch in the Catch statement defines the variable, scoped to this catch statement.

As shown below:

Try {    //  Illegal Operation to force an exception! }catch  (err) {    //  works!  //  referenceerror: ' Err ' not founde

The scope of err is only a catch statement, so Console.log (err) throws a Referenceerror exception.

Let keyword

The Let keyword was introduced in ES6. Let and var are used to define variables.

The difference is:

    1. var defines the scope of a variable as a function, and let defines a variable scoped to a code block.
    2. The location where the VAR definition occurs does not affect its references in the scope, and the variables defined by let are only referenced after they are defined.

As shown below:

var true ; if (foo) {    //  <--explicit blocklet        bar = foo * 2;         = something (bar);        Console.log (bar);     // Referenceerror
{   //  referenceerror!   Let bar = 2;}
Let loop

Let to define a variable in a for loop, this variable is scoped to the for loop.

As shown below:

 for (Let i=0; i<10; i++) {    //  referenceerror

Let's bind the scope of I to a for loop, and each time the loop is bound, it can be illustrated with the following code:

{let    j;      for (j=0; j<10; j + +) {        //  re-bound for each iteration!         Console.log (i);    }}
Const

The variable that is defined by the Const keyword introduced in ES6 is scoped to a code block.

Unlike the Let keyword, a const is defined as a constant and cannot be modified.

As shown below:

var true ; if (foo) {    var a = 2;     // block-scoped to the containing ' if '    // just fine!    // error!  //  3//  referenceerror!
Closed Package
What is a closure package

A closure is the ability of a function to access its ancestor scope when it is executed outside its static scope.

As shown below:

function foo () {    var a = 2;     function Bar () {        console.log (a);    }     return Bar;}
var baz =//  2--whoa, closure was just observed, man.

The Foo () function returns its intrinsic function bar ().

The function bar () is called outside its static scope (Baz ()) and still has access to variable a in its ancestor scope.

This capacity is called closures.

It can be said that the closure of Bar () contains the scope of Foo (). The original is: Function bar () has a closure over the scope of Foo ()).

Closures and loops

When closures and loops are tangled together, the situation becomes interesting.

Now implement a code output of 1 2 3 4 5, each time the output interval is 1 seconds.

Take a look at the following code:

 for (var i=1; i<=5; i++) {    function  timer () {        console.log (i);    }, I* );}

The function timer () accesses the variable I,timer () in its ancestor scope, which is called by the timer, and the call is in its static domain, thus constituting the closure.

The output of the code is five 6.

The reason is that the timer runs at the earliest in 1 seconds, when the loop is executed and the value of I becomes 6. Because I is scoped to the function where the for loop is located, the timer () five executions get the latest value of I.

Does calling the function expression immediately (iife) solve this problem?

The function expression creates a new scope, but does it solve the problem?

Take a look at the following code:

 for (var i=1; i<=5; i++) {    (function() {        function  timer () {            Console.log (i);        }, I*1000 );    }) ();}

After execution, the result is still five 6.

The reason is that although this function expression creates a new scope, the scope is empty, and the final timer () function refers to I in a higher-level scope.

What if I store the current value of I inside the function expression scope?

Take a look at the following code:

 for (var i=1; i<=5; i++) {    (function() {        var j = i;         function timer () {            console.log (j);        }, J*1000 );    }) ();}

Results after execution are 1 2 3 4 5

Because the function expression creates a new scope, five loops produce 5 copies of the scope, and J stores the different values for I in each copy.

the timer () function executes in different copies of the scope and prints out different values.

The above code is a more concise notation:

 for (var i=1; i<=5; i++) {    (function(j) {        function  timer () {            Console.log (j);        }, J*1000 );    }) (i);}

Can the Let keyword solve this problem?

Let's scope is block, can solve this problem?

Take a look at the following code:

 for (var i=1; i<=5; i++) {    //  yay, block-scope for closure!    function timer () {        console.log (j);    }, J*1000 );}

The execution result is 1 2 3 4 5.

Because the domain is a block, the values of J in the five copies of the five cycles are different, so the output values are different.

When you define a loop variable with let, the scope of the variable is a for loop, and each loop is bound once to the scope (producing a copy), so the above code can be further refined to:

 for (Let I=1; i<=5; i++) {    function  timer () {        console.log (i);    }, I* );}

javascript--scopes and closures

Related Article

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.