Deep understanding of JavaScript scopes and closures _ basics

Source: Internet
Author: User
Tags anonymous closure memory usage variable scope wrapper

Scope

Scopes are scoped to a variable and function, and all variables declared within a function in JavaScript are always visible in the body of the function, with global scope and local scope in JavaScript, but no block-level scope, and local variables have precedence over global variables, Take a few examples to understand the "unspoken rules" of scope in JavaScript (these are also frequently asked questions in a front-end interview).

1. Variable declaration advance
Example 1:

var scope= "global";
function Scopetest () {
  console.log (scope);
  var scope= "local" 
}
scopetest ();//undefined

The output here is undefined and there is no error, because the declaration in the function we mentioned earlier is always visible in the function body, and the function above is equivalent to:

var scope= "global";
function Scopetest () {
  var scope;
  Console.log (scope);
  Scope= "local" 
}
scopetest ();//local

Note that if you forget Var, the variable is declared as a global variable.

2. No block-level scopes

Unlike other languages we commonly use, there is no block-level scope in javascript:

function Scopetest () {
  var scope = {};
  if (scope instanceof Object) {
    var j = 1;
    for (var i = 0; i < i++) {
      //console.log (i);
    }
    Console.log (i); Output
  }
  Console.log (j);//Output 1

}

The scope of a variable in JavaScript is functional, meaning that all variables in a function are defined throughout the function, and this also brings some "unspoken rules" that we encounter with little attention:

var scope = "Hello";
function Scopetest () {
  console.log (scope);//①
  var scope = "No";
  Console.log (scope);//②
}

The value of the output at ① is undefined, it is insane, we have defined the value of global variables Ah, this place should not be hello? In fact, the above code is equivalent to:

var scope = "Hello";
function Scopetest () {
  var scope;
  Console.log (scope);//①
  scope = "no";
  Console.log (scope);//②
}

Declaration advance, global variable priority lower than local variables, according to these two rules is not difficult to understand why the output undefined.

Scope chain

In JavaScript, each function has its own execution context, and when the code executes in this environment, the scope chain of the variable object is created, and the scope chain is an object list or chain of objects that guarantees the orderly access of the variable object.
The front end of the scope chain is the variable object of the current code execution environment. Often referred to as "active objects", the lookup of a variable begins with the object of the first chain, and if the object contains variable attributes, stops the lookup, and if it does not, it continues to look up to the superior scope chain until the global object is found:

The step-by-step lookup of the scope chain also affects the performance of the program, the longer the variable scope chain, the greater the performance impact, which is one of the main reasons we try to avoid using global variables.

Closed Bag

Basic concepts
A scope is a prerequisite for understanding closures, which means that variables in external scopes are always accessible within the current scope.

function Createclosure () {
  var name = ' Jack ';
  return {
    setstr:function () {
      name = ' Rose ';
    },
    getstr:function () {return
      name + ': Hello ';
    }
  }
}
var builder = new Createclosure ();
Builder.setstr ();
Console.log (Builder.getstr ()); Rose:hello

The example above returns two closures in the function, both of which maintain references to external scopes, so that the variables in the external function can always be accessed regardless of where the call is made. A function defined inside a function adds an active object of an external function to its own scope chain, so the properties of the external function can be accessed by an intrinsic function in the above instance, which is also a way for JavaScript to simulate private variables.

Note: Because closures can have additional function scopes (internal anonymous functions carry the scope of external functions), closures can take up more memory space than other functions, and excessive use can lead to increased memory usage.

Variables in closures

When using closures, the closure can only get the last value of the internal function due to the effect of the scope chain mechanism, and one side effect of this is that if the intrinsic function is in a loop, then the value of the variable is always the last value.

  This example is not reasonable and has a certain latency factor, which is mainly to illustrate the problems existing in the closure loop
  function Timemanage () {for
    (var i = 0; i < 5; i++) {
      settimeout (func tion () {
        console.log (i);
      },1000)
    };
  }

The above program did not enter 1-5 of the number as we expected, but 5 times it all output 5. Let's look at an example:

function Createclosure () {
  var result = [];
  for (var i = 0; i < 5; i++) {
    Result[i] = function () {return
      i;
    }
  }
  return result;
}

The call to Createclosure () [0] () returns 5,createclosure () [4] () The return value is still 5. The above two examples show the problem of closures in the use of internal functions with loops: Because each function's scope chain holds active objects to external functions (Timemanage, createclosure), so they all refer to the same variable i, when the external function returns, At this point the I value is 5, so the value of each function I inside is also 5.
So how do you solve the problem? We can force the return of the expected result through an anonymous wrapper (an anonymous self-executing function expression):

function Timemanage () {for
  (var i = 0; i < 5; i++) {
    (function (num) {
      settimeout) (function () {
        console. Log (num);
      }, 1000);
    }) (i);
  }

or return an anonymous function assignment in the closure anonymous function:

function Timemanage () {for
  (var i = 0, I < i++) {
    settimeout (function (e) {return
      function () {
   console.log (e);
      }
    ) (i), 1000)
  }
}
//timemanager (); Output 1,2,3,4,5
function createclosure () {
  var result = [];
  for (var i = 0; i < 5; i++) {
    Result[i] = function (num) {return
      function () {
        console.log (num);
      }
    } (i);
  }
  return result;
}
Createclosure () [1] () output 1;createclosure () [2] () Output 2

Whether it's an anonymous wrapper or a nested anonymous function, the principle is that the function is passed by value, so the value of the variable i is copied to the argument num, and an anonymous function is created inside the anonymous function to return num so that each function has a copy of Num and does not affect each other.

This in the closure

Special attention should be paid to using this in closures, and a slight carelessness may cause problems. Usually we understand that the This object is a run-time based function-bound, and the This object in the global function is the Window object, and when the function is called as a method in the object, this is equal to this object (TODO). Because the scope of the anonymous function is global, this usually points to the Global object window for the closure:

var scope = "global";
var object = {
  scope: ' Local ',
  getscope:function () {return
    function () {return
      this.scope
    }
}}}

Call Object.getscope () () returns a value of global instead of our expected local, we said that the internal anonymous function in the closure carries the scope of the external function, so why not get this from the external function? This and arguments are created automatically when each function is invoked, and the internal anonymous function searches for the variables in the active object that we want, so stopping the lookup in the external function and never directly accessing the variables in the external function. In summary, when a function in a closure is called as a method call to an object, it is particularly noted that this point of the anonymous function inside the method points to a global variable.

Fortunately, we can solve this problem very simply by storing the scope of this external function in a variable that can be accessed by a closed package:

var scope = "global";
var object = {
  scope: ' Local ',
  getscope:function () {
    var = this;
    return function () {return
      that.scope
    }
}} Object.getscope () () The return value is local.

Memory and performance

Because the closure contains scope chain references that are the same as the function runtime context, therefore, there is a certain negative effect, when the active object and Run-time context in the function destroyed, because of the need to still have a reference to the active object, causing the active object can not be destroyed, which means that the closure than ordinary functions occupy more memory space, In IE browser may also cause a memory leak problem, as follows:

 function Bindevent () {
  var target = document.getElementById ("Elem");
  Target.onclick = function () {
    console.log (target.name);
  }
 }

In the example above, the anonymous function produces a reference to the target object of the external object, and as long as the anonymous function exists, the reference does not disappear and the target of the external function is not destroyed, which produces a circular reference. The solution is to reduce the circular references to external variables and manually reset objects by creating Target.name replicas:

 function Bindevent () {
  var target = document.getElementById ("Elem");
  var name = Target.name;
  Target.onclick = function () {
    console.log (name);
  }
  target = null;
 }

If there is access to external variables in the closure, it undoubtedly increases the lookup path of the identifier, and in certain cases, it can also cause the loss of performance. The solution to this kind of problem we have mentioned before: as far as possible to save the external variables to local variables, reduce the scope chain of the lookup length.

Conclusion: Closures are not unique to JavaScript, but they are unique in JavaScript, and we can use closures to define private variables in JavaScript and even to mimic block-level scopes, but we need to understand the problems we have in the process of using closures, This is to avoid unnecessary problems.

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.