Brief overview of scope and context in javascript _ basic knowledge

Source: Internet
Author: User
The following details the differences between the context and scope in javascript and how to use them in various design patterns. Do not miss the scope and context in javascript) is the uniqueness of the language, thanks in part to the flexibility they bring. Each function has different variable context and scope. These concepts are the backing of some powerful Design Patterns in javascript. However, this also brings great confusion to developers. The following fully reveals the differences between context and scope in javascript and how to use them in various design patterns.

Context vs Scope

The first problem to be clarified is that context and scope are different concepts. Over the years, I have noticed that many developers often confuse these two terms and incorrectly describe one as another. In all fairness, these terms have become very messy.

Each function call has a corresponding scope and context. Basically, the range is function-based, while the context is object-based ). In other words, the scope is related to the access to variables during each function call, and each call is independent. Context is always the value of the keyword this, which is a reference to the object that calls the current executable code.

Variable Scope

Variables can be defined in local or global scopes, which leads to access of variables at runtime from different scopes. Global variables must be declared in the external body of the function and exist throughout the operation. They can be accessed and modified in any scope. Local variables are defined only in the function body, and each function call has different scopes. This topic is an operation that only performs value assignment, evaluation, and peer value in a call. It cannot access values outside the scope.

Currently, javascript does not support block-level scopes. Block-level scopes refer to defining variables in if statements, switch statements, cyclic statements, and other statement blocks. This means that variables cannot be accessed outside of the statement block. Currently, any variables defined in the statement block can be accessed outside the statement block. However, this situation will soon change, and the let keyword has been officially added to the ES6 specification. Instead of the var keyword, you can declare local variables as block-level scopes.

"This" context

The context usually depends on how a function is called. When a function is called as an object's method, this is set to the object that calls the method:

The Code is as follows:


Var object = {
Foo: function (){
Alert (this = object );
}
};

Object. foo (); // true


The same principle applies to creating an object instance through the new operator when calling a function. When called in this way, the value of this will be set to the newly created instance:

The Code is as follows:


Function foo (){
Alert (this );
}

Foo () // window
New foo () // foo


When an unbound function is called, this is set as a global context or window object by default (if in the browser ). However, if a function is executed in strict mode ("use strict"), the value of this is set to undefined by default.
Execution context and scope chain

Javascript is a single-threaded language, which means that only one thing can be done in the browser at the same time. When the javascript interpreter executes the code at the beginning, it defaults to a global context. Each time you call a function, a new execution context is created.

Obfuscation often occurs here. The term "execution context" here refers to scope, not the context discussed above. This is the name of the bucket. However, this term is defined in the ECMAScript specification.

Each time a new execution context is created, it is added to the top of the scope chain and becomes the execution or call stack. The browser always runs in the current execution context at the top of the scope chain. Once completed, it (the current execution context) will be removed from the top of the stack and the control will be returned to the previous execution context. For example:

The Code is as follows:


Function first (){
Second ();
Function second (){
Third ();
Function third (){
Fourth ();
Function fourth (){
// Do something
}
}
}
}
First ();


Running the code above will cause the nested function to be executed from top down until the fourth function. At this time, the scope chain is: fourth, third, second, first, global. The fourth function can access global variables and any variables defined in the first, second, and third functions, just like accessing your own variables. Once the fourth function is executed, the fourth dizzy context will be removed from the top of the scope chain and the execution will return to the thrid function. This process continues until all code has been executed.

Variable naming conflicts between different execution contexts are resolved by climbing the scope chain from local to global. This means that local variables with the same name have a higher priority in the scope chain.

To put it simply, every time you try to access a variable in the context of the function execution, the search process always starts from its own variable object. If no variable to be searched is found in your variable object, continue searching for the scope chain. It checks the variable objects in each execution context to search for values that match the variable name.

Closure

When a nested function is accessed outside the definition (scope), so that it can be executed after the external function is returned, a closure is formed. It maintains (in internal functions) access to local variables, arguments, and function declarations in external functions. Encapsulation allows us to hide and protect execution contexts from external scopes, expose public interfaces, and perform further operations through interfaces. A simple example looks like this:

The Code is as follows:


Function foo (){
Var local = 'private variable ';
Return function bar (){
Return local;
}
}

Var getLocalVariable = foo ();
GetLocalVariable () // private variable


The most popular closure type is the widely known module model. It allows you to simulate public, private, and privileged members:

The Code is as follows:


Var Module = (function (){
Var privateProperty = 'foo ';

Function privateMethod (args ){
// Do something
}

Return {

PublicProperty :"",

PublicMethod: function (args ){
// Do something
},

PrivilegedMethod: function (args ){
PrivateMethod (args );
}
}
})();


The module is actually similar to a singleton. A pair of parentheses are added at the end of the module. Execute the module immediately after the interpreter is interpreted (execute the function immediately ). The only externally available Member of the closure that executes the upper and lower bits is the common methods and attributes (for example, Module. publicMethod) in the returned object ). However, all private attributes and methods will exist throughout the life cycle of the program. Because (closures) enable the execution context to receive protection, interaction with variables should be implemented through a public method.

Another type of closure is called immediately-invoked function expression IIFE. It is nothing more than self-invoked anonymous function in the window context ).

The Code is as follows:


Function (window ){

Var a = 'foo', B = 'bar ';

Function private (){
// Do something
}

Window. Module = {

Public: function (){
// Do something
}
};

}) (This );


This expression is very useful for protecting global namespaces. All variables declared in the function body are local variables, and they are maintained throughout the runtime environment through closures. This method of encapsulating source code is very popular for programs and frameworks. It usually exposes a single global interface to interact with the outside world.

Call and Apply

These two simple methods are built in all functions, allowing you to execute functions in a custom context. The call function requires a list of parameters while the apply function allows you to pass parameters as Arrays:

The Code is as follows:


Function user (first, last, age ){
// Do something
}
User. call (window, 'john', 'doe ', 30 );
User. apply (window, ['john', 'doe ', 30]);


The execution result is the same. The user function is called in the window context and three identical parameters are provided.

ECMAScript 5 (ES5) introduces Function. prototype. the bind method controls the context. It returns a new function, which is permanently bound to the first parameter of the bind method, regardless of how the function is called. It uses closures to modify the context of a function. The following solutions are provided for unsupported browsers:

The Code is as follows:


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 );
}
}
}


It is often used in context loss: Object-oriented and event processing. This is necessary because the addEventListener method of the node keeps the context of the function execution as the node to which the event processing is bound. This is very important. However, if you use advanced object-oriented technology and need to maintain the context of the callback function as an example of a method, you must adjust the context manually. This is the convenience of bind:

The Code is as follows:


Function MyClass (){
This. element = document. createElement ('P ');
This. element. addEventListener ('click', this. onClick. bind (this), false );
}

MyClass. prototype. onClick = function (e ){
// Do something
};


When reviewing the source code of the bind function, you may notice that the following line of relatively simple code calls an Array method:

The Code is as follows:


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


Interestingly, the arguments object is not an array, but it is often described as an array-like object, which is very similar to nodelist (document. the result returned by the getElementsByTagName () method ). They include the lenght attribute and the values can be indexed, but they are still not arrays because they do not support native array methods, such as slice and push. However, because they have similar behavior with arrays, array methods can be called and hijacked. If you want to execute the array method in the context of the class array, refer to the above example.

This method of calling other object methods is also applied to object-oriented systems. When imitating classical inheritance (class inheritance) in javascript ):

The Code is as follows:


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


By calling the superclass method in a subclass (MyClass) instance, we can reproduce this powerful design pattern.

Conclusion

It is important to understand these concepts before you begin to learn advanced design patterns, because scopes and contexts play an important and fundamental role in modern javascript. Whether we talk about closures, object-oriented, and inheritance or various native implementations, context and scope play an important role. If your goal is to master the javascript language and gain a deep understanding of its composition, the scope and context should be your starting point.

Supplemented by translators

The bind function implemented by the author is incomplete. parameters cannot be passed when the function returned by bind is called. The following Code fixes this problem:

The Code is as follows:


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); // fixed
}
}
}

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.