JavaScript scope (scope) and contexts (context)

Source: Internet
Author: User
Tags addall closure naming convention variable scope

JavaScript's implementation of scope and context is a very unique place in this language, thanks in part to its special flexibility. Functions can receive different contexts and scopes. These concepts provide a solid foundation for many of the powerful design patterns in JavaScript. However, this concept is also very easy to confuse developers. To this end, this article will be a comprehensive analysis of these concepts, and explain how different design patterns are using them.

Scope (scope) and contexts (context)

The first thing to know is that the context and scope are two completely different concepts. Over the years, I've found that many developers confuse the two concepts (including myself) by confusing the two concepts incorrectly. To be fair, many of these terms have been used in disarray over the years.

Each invocation of a function has a scope and context that is closely related to it. Fundamentally, scopes are function-based, and contexts are object-based. In other words, scopes involve variable access in the called function, and different invocation scenarios are not the same. The context is always this the value of the keyword, which is a reference to the object that owns (controls) the currently executing code.

Variable scope

A variable can be defined in a local or global scope, which establishes the scope of different scopes for the variable's accessibility during runtime. Any defined global variable means that it needs to be declared outside the body of the function and that it survives the entire runtime (runtime) and can be accessed in any scope. Before ES6, local variables can only exist in the function body, and each call to a function has a different scope range. A local variable can be assigned, retrieved, manipulated only within the scope of its called period.

It is important to note that before ES6, JavaScript does not support block-level scopes, which means that if switch for while block-level scopes cannot be supported in statements, statements, loops, loops. In other words, JavaScript before ES6 does not build block-level scopes like Java (variables cannot be accessed outside the statement block). However, starting with ES6, you can define variables by keyword, let which corrects the var disadvantages of keywords, allows you to define variables like the Java language, and supports block-level scopes. See two examples:

Before ES6, we used var keywords to define variables:

function func () {  if (true) {    var tmp = 123;  }  Console.log (TMP); 123}

Access is possible because var the keyword declares a variable that has a variable promotion process. In ES6 scenarios, it is recommended to let define variables using keywords:

function func () {  if (true) {let    tmp = 123;  }  Console.log (TMP); REFERENCEERROR:TMP is not defined}

This way, many mistakes can be avoided.

What is thisContext

The context usually depends on how the function is called. When a function is called as a method in an object, this it is set to the object on which the method is called:

var obj = {    foo:function () {        alert (this = = obj);        }};o Bj.foo (); True

This guideline also applies when you use the operator to new create an instance of an object when the function is called. In this case, the values inside the scope of the function this are set to the newly created instance:

function foo () {    alert (this);} New Foo ()//Foofoo ()//window

When you call a binding function, this by default it is the global context, which in the browser points to the window object. It is important to note that ES5 introduces the concept of strict mode, and if strict mode is enabled, the context defaults to undefined .

Execution environment (execution context)

JavaScript is a single-threaded language, meaning that only one task can be performed at the same time. When the JavaScript interpreter initializes the execution code, it first enters the global Execution Environment (execution context) by default, and from this point on, each invocation of the function creates a new execution environment.

This is often confusing for novices, and here's a new term-execution environment (execution context) that defines the other data that variables or functions have access to, and determines their behavior. It is more biased towards the role of scopes than the context we discussed earlier. It is important to carefully distinguish between the two concepts of execution environment and context (note: English is easily confusing). To tell you the truth, this is a very bad naming convention, but it is ECMAScript specification, you should follow it.

Each function has its own execution environment. When the execution flow enters a function, the environment of the function is pushed into an environment stack (execution stack). After the function is executed, the stack pops up its environment and returns control to the previous execution environment. The execution flow in the ECMAScript program is controlled by this convenient mechanism.

The execution environment can be divided into two phases: Create and execute. During the creation phase, the parser first creates a variable object (variable object, also known as the active object activation objects), which consists of variables, function declarations, and parameters defined in the execution environment. At this stage, the scope chain is initialized and this the value is finally determined. During the execution phase, the code is interpreted to execute.

Each execution environment has a variable object associated with it (variable object), and all variables and functions defined in the environment are stored in the object. It is necessary to know that we cannot access this object manually, only the parser can access it.

Scope Chain (the scope Chain)

When code executes in an environment, a scope chain of variable objects is created (scope chain). The purpose of a scope chain is to ensure an orderly access to all variables and functions that the execution environment has access to. The scope chain contains variable objects that correspond to each execution environment in the environment stack. Through the scope chain, you can determine the access of variables and the parsing of identifiers. Note that the variable object for the global execution environment is always the last object in the scope chain. Let's look at an example:

var color = "Blue"; function ChangeColor () {  var anothercolor = "Red";  function Swapcolors () {    var tempcolor = Anothercolor;    Anothercolor = color;    color = Tempcolor;    You can access color, Anothercolor, and Tempcolor  }  //Here you can access the color and anothercolor, but you cannot access Tempcolor  swapcolors ();} ChangeColor ();//This can only be accessed Colorconsole.log ("Color is now" + color);

The above code consists of three execution environments: the global environment, the local environment of the ChangeColor (), and the local environment of the swapcolors (). The scope chain for the above program is as follows:

From the discovery. The internal environment can access all external environments through the scope chain, but the external environment cannot access any variables and functions in the internal environment. The linkages between these environments are linear and sequential.

For identifier parsing (variable name or function name search) is the process of searching identifiers at the level of the scope chain. The search process always starts at the front end of the scope chain and then backwards back (the global execution Environment) backward until the identifier is found.

Closed Package

Closures are functions that have access to variables in the scope of another function. In other words, when a nested function is defined within a function, it forms a closure that allows the nested function to access the variables of the outer function. By returning nested functions, you are allowed to maintain access to local variables, parameters, and inner function declarations in external functions. This encapsulation allows you to hide and protect the execution environment in an external scope and expose the public interface to perform further operations through the public interface. You can see a simple example:

function foo () {    var localvariable = ' private variable ';    return function bar () {        return localvariable;    }} var getlocalvariable = foo (); getlocalvariable ()//private variable

One of the most popular closures types in module mode, which allows you to emulate public, private, and privileged members:

var Module = (function () {    var privateproperty = ' foo ';    function Privatemethod (args) {        //do something    }    return {        publicproperty: ',        Publicmethod: function (args) {            //do Something        },        privilegedmethod:function (args) {            return Privatemethod (args );        }    };}) ();

The module resembles a singleton object. As the anonymous function is used in the code above (function() { ... })(); , it is executed immediately when the compiler parses it. The only objects that can be accessed outside the execution context of the closure are the public methods and properties that are located in the returned object. However, because the execution context is preserved, all private properties and methods will always exist throughout the application's lifecycle, which means that we can interact with them only through public methods.

Another type of closure is called an immediately executing function expression (iife). It's simple, but it's a self-executing anonymous function in the Global environment:

(function (window) {    var foo, bar;    function Private () {        //do something    }    window. Module = {        public:function () {            //do Something         }    };}) (this);

This expression is useful for protecting the global namespace from variable pollution, which isolates variables from the global namespace in the form of a constructor scope and enables them to exist throughout the runtime (runtime) in the form of closures. In many applications and frameworks, this way of encapsulating source code is very popular, usually by exposing a single global interface to external interaction.

Call and apply

These two methods are built into all functions (they are Function the object's prototype method) and allow you to execute functions in the context of a custom. The difference is that the call function requires a list of arguments, and the apply function requires you to provide an array of arguments. As follows:

var o = {};function F (A, B) {  return a + b;} The function f as the method of O, is actually re-set the function F of the context F.call (O, 1, 2);    3f.apply (O, [1, 2]); 3

Two results are the same, the function is f called in the context of the object and o provides two identical parameters 1 and 2 .

A method is introduced in ES5 that Function.prototype.bind controls the execution context of the function, it returns a new function, and the new function is permanently bound to the bind object specified by the first parameter of the method, regardless of how the function is used. It directs the function into the correct context by closing the packet. For the lower version of the browser, we can simply implement it as follows (Polyfill):

if (! (' Bind ' in Function.prototype)} {    Function.prototype.bind = Function () {        var fn = this,             context = arguments[0],             args = Array.prototype.slice.call (arguments, 1);        return function () {            return fn.apply (context, args.concat (arguments));}}}    

bind()Methods are often used in scenarios where context is lost, such as object-oriented and event handling. This is done because the method of the node addEventListener always executes the callback function in the context of the node to which the event handler is bound, which is what it should behave. However, if you want to use advanced object-oriented techniques, or if you want your callback function to be an instance of a method, you will need to manually adjust the context. This is where bind the approach comes in handy:

function MyClass () {    this.element = document.createelement (' div ');    This.element.addEventListener (' Click ', This.onClick.bind (this), false);} MyClass.prototype.onClick = function (e) {    //do something};

Recalling the bind source code of the above method, you may notice that there are two calls involved Array in the slice method:

Array.prototype.slice.call (arguments, 1); [].slice.call (arguments);

We know that an arguments object is not a real array, but rather a class array object that has the length property and that the values can be indexed, but they do not support native array methods, such as slice and push . However, because they have a similar array of behaviors, the methods of arrays can be called and hijacked, so we can do this in a way similar to the code above, the core of which is to use call methods.

This technique of invoking other object methods can also be applied to object-oriented, and we can simulate classic inheritance in javascript:

MyClass.prototype.init = function () {    //Call the superclass Init method in the context of the "MyClass" instance    M YSuperClass.prototype.init.apply (this, arguments);}

That is, a call apply method that uses or MyClass invokes a superclass () in an instance of a subclass () MySuperClass .

Arrow functions in the ES6

The arrow function in ES6 can be used as Function.prototype.bind() a substitute. Unlike the normal function, the arrow function does not have its own this value, and its this value inherits from the perimeter scope.

For normal functions, it will always automatically receive a this value, this depending on how it is called. Let's look at an example:

var obj = {  //...  Addall:function (Pieces) {    var self = this;    _.each (pieces, function (piece) {      self.add (piece);    });}  ,  //...}

In the above example, the most straightforward idea is to use it directly this.add(piece) , but unfortunately, in JavaScript you cannot do this because each the callback function does not inherit the value from the outer layer this . In the callback function, this the value is window or undefined , therefore, we use a temporary variable self to import the external this value internally. We also have two ways to solve this problem:

Using the bind () method in ES5

var obj = {  //...  Addall:function (Pieces) {    _.each (pieces, function (piece) {      this.add (piece);    }. Bind (this));  },  //...}

Using the arrow functions in ES6

var obj = {  //...  Addall:function (Pieces) {    _.each (pieces, piece = This.add (piece));  },  //...}

In the ES6 version, the addAll method obtains the value from its caller this , and the intrinsic function is an arrow function, so it integrates the values of the outer scope this .

Note: For callback functions, in the browser, the callback function is in this window or undefined (strict mode), whereas in node. js, the callback function this is global . The instance code is as follows:

function Hello (A, callback) {  callback (a);} Hello (' Weiwei ', function (a) {  Console.log (this = = = global);//True  Console.log (a);//Weiwei});
Summary

Before you learn advanced design patterns, it is important to understand these concepts because scopes and contexts play the most basic role in modern JavaScript. Whether we're talking about closures, object-oriented, inheritance, or a variety of native implementations, the context and scope play a critical role. If your goal is to master the JavaScript language and understand its various components in depth, then the scope and context are your starting point.

JavaScript scope (scope) and contexts (context)

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.