Principle and Example Analysis of javascript closure objects (I), javascript

Source: Internet
Author: User

Principle and Example Analysis of javascript closure objects (I), javascript

Reading, understanding, thinking, practice, practice, and thinking...

Javascript advanced features include: scope, closure, Object

----------------------------------------------- Scope ----------------------------------------------------------------------------------------

ScopeScope is an important concept in structured programming language. It determines the visible range and lifecycle of variables.
Correct use of the scope can make the code clearer and easier to understand. The scope can reduce naming conflicts and reduce garbage collection.
. Unlike common languages such as C, C ++, and Java, the scope of JavaScript is not a block enclosed by curly brackets.
Block scope is often ignored by most people, leading to inexplicable errors. For example
The following code displays an undefined variable error in most C-like languages, but is completely legal in JavaScript:
If (true ){
Var somevar = 'value ';
}
Console. log (somevar); // output value
This is becauseThe JavaScript scope is entirely determined by the function.The curly braces in the if and for statements are not independent.
.


ScopeScope includes the function scope and global scope.

Function Scope: Unlike most C-like languages, a block of code enclosed by curly braces is a scope, and the JavaScript scope is defined by functions,Variables defined in a function are only visible to the function., We call it a letter
Number scope. When a variable is referenced in a function, JavaScript first searches for the current function scope, or
If not found, search for its upper-level scope until the global scope. Let's look at a simple
Example:
Var v1 = 'v1 ';
Var f1 = function (){
Console. log (v1); // output v1
};
F1 ();
Var f2 = function (){
Var v1 = 'local ';
Console. log (v1); // output local
};
F2 ();
The above example is very clear,JavaScript function definitions can be nested. Each layer is a scope, variable
The search order is from inside to outside.
. The following example may be confusing:
Var scope = 'global ';
Var f = function (){
Console. log (scope); // output undefined
Var scope = 'F ';
}
F ();
The above code may be different from what you expected. It does not output global, but undefined. Why?
This is a feature of JavaScript. In the order of scope search, the scope is changed when the console. log function accesses the scope.
When the amount is used, JavaScript will first search for the scope of function f, which happens to find the scope variable in the scope of function f,
Therefore, the scope defined in the upper-level scope is blocked, but when the console. log statement is executed, the scope also
It is not defined or initialized, so the undefined value is obtained.
We can also understand from another perspective: for developers, when accessing undefined variables or definitions
When an initialized variable exists, the obtained values are all undefined.
So we can think that no matter what location in the function
Variables defined by Party A are defined when they enter the function, but they are not initialized until the line in which var is located.
In this case, all referenced values are undefined. (In fact, the internal implementation of JavaScript is not like this.
Variables that define variables and values as undefined are different. )
Function scope nesting
Next, let's look at a slightly complex example:

Var f = function (){
Var scope = 'f0 ';
(Function (){
Var scope = 'f1 ';
(Function (){
Console. log (scope); // output f1
})();

})();
};
F ();
The above is an example of function scope nesting. We reference the scope variable in the innermost function.
Domain Search: the scope variable defined in its parent scope is found.
One thing to note: the nesting relationship of function scopes is determined during definition, rather than during calling.
Yes,JavaScript scopes are static scopes, also known as lexical scopes. This is because of the nested relationship of scopes.
To be determined during syntax analysis, instead of waiting for confirmation at runtime
. The following example illustrates all of this:
Var scope = 'top ';
Var f1 = function (){
Console. log (scope );
};
F1 (); // output top
Var f2 = function (){
Var scope = 'F2 ';
F1 ();
};
F2 (); // output top
In this example, f1 called by f2 finds the definition of scope, which is defined in the parent scope.
Instead of the scope variable defined in f2. This shows that the nested relationship of the scope is not called
Is determined at the time of definition.


Global scope:In JavaScript, a special object is called a global object. This object corresponds to global in Node. js.
The window object in the browser. Because all attributes of the global object are visible anywhere,
Therefore, this object is also called global scope. Variables in the global scope can be directly cited in any function.
Instead of using global objects.


Variables that meet the following conditions belong to the global scope:

Variables defined at the outermost layer;

Attributes of global objects;

Variables implicitly defined anywhere (undefined variables directly assigned values ).


Note that, third, variables implicitly defined anywhere are defined in the global scope,No
Declare directly assigned variables using var
. This is often forgotten, and an important principle of Modular programming is
Avoid using global variables, so we should not implicitly define variables anywhere.

-----------------------------------------------------Closure--------------------------------------------------------------------------

Closure (closure)It is a concept in functional programming. In 1960s, it was the first language to implement closures.
Scheme is a dialect of LISP. The closure feature is widely used in other languages.
The strict definition of closures is"A collection composed of functions (environments) and closed Free Variables. "This definition
It is a bit obscure to everyone, so let's first explain what is a closure through examples and less rigorous explanations,
Then, we will give an example to illustrate the classic use of some closures.

What is a closure?

In general, every function in JavaScript is a closure, but in general, nested functions are more
The closure feature is shown in the following example:
Var generateClosure = function (){
Var count = 0;
Var get = function (){
Count ++;
Return count;
};
Return get;
};
Var counter = generateClosure ();
Console. log (counter (); // output 1
Console. log (counter (); // output 2
Console. log (counter (); // output 3
In this Code, the generateClosure () function has a local variable count, and the initial value is 0. Another one
A function called get. get changes the count in its parent scope, that is, generateClosure () function.
Increase 1 and return the value of count. The returned value of generateClosure () is the get function. Outside, we
The counter variable calls the generateClosure () function and obtains its return value, that is, the get function.
Count, and then call counter () several times repeatedly. We find that each return value increases by 1.
Let's take a look at the features of the above example. According to the general imperative programming thinking, count is
The variable in the generateClosure function. Its life cycle is when generateClosure is called.
When generateClosure is returned from the call stack, the space applied for by the count variable is released.Problem

Yes. After the generateClosure () call is complete, counter () References "released" count

Instead of making an error, the counter () variable is modified and the count value is returned each time it is called. How is this
What's going on?

This is the so-called closure feature.When a function returns a function defined internally, a closure is generated.,
The closure includes not only the returned function, but also the definition environment of the function. In the above example,When the Function
When the internal function get of generateClosure () is referenced by an external variable counter, counter and
The local variable of generateClosure () is a closure.
. If it is not clear enough, the following example can help
You understand:
Var generateClosure = function (){
Var count = 0;
Var get = function (){
Count ++;
Return count;
};
Return get;
};
Var counter1 = generateClosure ();
Var counter2 = generateClosure ();
Console. log (counter1 (); // output 1
Console. log (counter2 (); // output 1
Console. log (counter1 (); // output 2
Console. log (counter1 (); // output 3
Console. log (counter2 (); // output 2
The above example explains how the closure is generated: counter1 and counter2 call generate-
The Closure () function generates two Closure instances. The count variables referenced inside them belong to their respective
Running environment. We can understand that when generateClosure () returns the get function
The internal variable (that is, the count variable) of the generateClosure () function that can be referenced is also returned, and
Generate a copy in the memory, and generateClosure () returns the two instance counter1 of the Function
And counter2 are mutually independent.

Closure purpose:

 1. nested callback Functions
Closures have two main functions: one is to implement nested callback functions, and the other is to hide the details of objects. Let's take a look.
This section describes the nested callback functions. The following code uses MongoDB in Node. js to implement
Simple addition of user features:

Exports. add_user = function (user_info, callback ){
Var uid = parseInt (user_info ['uid']);
Mongodb. open (function (err, db ){
If (err ){
Callback (err );
Return
}
Db. collection ('users ',
Function (err, collection ){
If (err ){
Callback (err );
Return
}
Collection. ensureIndex ("uid ",
Function (err ){
If (err ){
Callback (err );
Return
}
Collection. ensureIndex ("username ",
Function (err ){
If (err ){
Callback (err );
Return
}
Collection. findOne ({
Uid: uid
},
Function (err ){
If (err ){
Callback (err );
Return
}
If (doc ){
Callback ('occupied ')
} Else {
Var user = {
Uid: uid,
User: user_info,


};
Collection. insert (user,
Function (err ){
Callback (err)
})
}
})
})
})
})
})
};

If you are not familiar with Node. js or MongoDB, you don't need to understand the details.
. This Code uses the layer-by-layer nesting of closures, and each layer of nesting is a callback function. Callback letter
The number is not executed immediately, but is called back by the function of the request after the corresponding request is processed. We can see that in the nested
Each layer references callback, And the innermost layer uses the uid variable defined by the outer layer. Because of closures
The existence of the mechanism, even if the outer function has been executed, the applied variables in its scope will not be released, because
Functions may also reference these variables, so that nested asynchronous Callbacks are perfectly implemented.


2. Implement Private Members

We know that JavaScript objects do not have private attributes. That is to say, every attribute of an object is exposed to external entities.
. This may cause security risks. For example, if the user of an object directly modifies an attribute,
Causes are damaged. JavaScript uses conventions to underline all private attributes (for example, _ myPrivateProp ),

Indicates that this attribute is private and the external object should not be read or written directly. However, this is just an informal convention, assuming that the object
Users do not do this. Is there a stricter mechanism? The answer is yes, which can be achieved through closures. Let's look at it again.
Let's look at the previous example:
Var generateClosure = function (){
Var count = 0;
Var get = function (){
Count ++;
Return count;
};
Return get;
};
Var counter = generateClosure ();
Console. log (counter (); // output 1
Console. log (counter (); // output 2
Console. log (counter (); // output 3
We can see that only counter () can be called to access the count variable in the closure and follow the rules
Increase the value by 1. In addition, it is impossible to find the count variable in other ways. Inspired by this simple example,
We can encapsulate an object with a closure and return only one "accessor" object to hide the details.
For more information about implementing private members of JavaScript objects, see http://javascript.crockford.com/private.html.

-------------------------------------------------------------------Object-------------------------------------------------------------

When it comes to object-oriented programming languages, it immediately reminds people of static and strong-type languages such as C ++ and Java,
As well as scripting languages such as Python and Ruby, they share the characteristics of class-based object-oriented. Speaking of JavaScript,
Few people think of its object-oriented features. Some people even say it is not an object-oriented language because it has no class. No
Wrong, JavaScript really has no class, but JavaScript is an object-oriented language. JavaScript only contains objects.
Is an object, not a class instance.
Because most objects in object-oriented languages are class-based, examples and objects of classes are often obfuscated.
. Objects are examples of classes, which are true in most languages, but not in JavaScript.
Objects in JavaScript are prototype-based, so many people are very confused when learning JavaScript objects. Pass
In this section, we will re-understand the objects in JavaScript and fully understand the essence of prototype-Based Object-Oriented.

Create and access

Objects in javaScript are actually an associated array composed of attributes. attributes are composed of names and values. Values
Can be any data type, function, or other objects. Note that JavaScript features functional programming,
Therefore, a function is also a variable, and most of the time it is not distinguished from the general data type.

In JavaScript, you can use the following method to create a simple object:
Var foo = {};
Foo. prop_1 = 'bar ';
Foo. prop_2 = false;
Foo. prop_3 = function (){
Return 'Hello world ';
}
Console. log (foo. prop_3 ());
In the above Code, an object is created through var foo = {}; and Its Reference is assigned to foo,
Use foo. prop1 to obtain its members and assign values, where {} is the representation of the object literal, you can also use var
Foo = new Object () to explicitly create an Object.
1. Access Object members using Correlated Arrays
We can also use the associated array mode to create objects. The above code is changed:
Var foo = {};
Foo ['prop1'] = 'bar ';
Foo ['prop2'] = false;
Foo ['prop3'] = function (){
Return 'Hello world ';
}
In JavaScript, the use of the period operator is equivalent to the associated array reference, that is, any object (including
These two modes can be used.The advantage of using Correlated arrays is that we do not know the property name of the object.
Variable can be used as the index of the associated array.
. For example:
Var some_prop = 'prop2 ';
Foo [some_prop] = false;

2. Create an object using the object initialization Tool
The above method only gives you an understanding of the definition of JavaScript objects.
The following is a more compact and clear method:
Var foo = {
'P1': 'bar ',
Prop2: 'false ',
Prop3: function (){
Return 'Hello world ';
}
};
This defined method is called the object initiator. Note: whether the object attribute name is cited when the initializer is used
It is optional. quotation marks are not required unless the attribute name contains spaces or other characters that may cause ambiguity.


Constructor
The object creation method described in the previous section has a weakness, that is, the code for creating an object is one-time. If I
They want to create multiple planned objects, have several fixed attributes and methods, and can initialize them, just like the C ++ language.
In the same way, what should we do? Don't worry. JavaScript provides constructor. Let's take a look
Create complex objects.
Function User (name, uri ){
This. name = name;
This. uri = uri;
This. display = function (){
Console. log (this. name );
}
}
The above is a simple constructor. Next we use the new statement to create an object:
Var someuser = new User ('byvoid', 'HTTP: // www.byvoid.com ');
Then you can access the attributes and methods of this object through someuser.

Context object

In JavaScript, the context object is the this pointer, that is, the environment where the called function is located. Context object
The function is used to reference the object itself that calls a function. Any function in JavaScript is called by an object.
This pointer is very important because it includes global objects.

In the previous code using the constructor, we have seen how to use this. The following code can be better clarified:
Describe how to use the context object:

Var someuser = {
Name: 'byvoid ',
Display: function (){
Console. log (this. name );
}
};
Someuser. display (); // output byvoid
Var foo = {
Bar: someuser. display,
Name: 'foobar'
};
Foo. bar (); // output foobar

The functional programming feature of JavaScript allows functions to be assigned, transferred, and computed just like common variables.
In the code above, the bar attribute of the foo object is the someuser. display function, using foo. bar ()
When calling, the functions of bar and foo objects look no different.This pointer does not belong to a function, and
Is the object to which the function is called.

In JavaScript, in essence, a variable of the function type is a reference to this function entity.Reference
The value assignment does not copy objects.
. We canIt is different to call this function through any reference of the function.
Only Context
. The following example can help us understand:
Var someuser = {
Name: 'byvoid ',
Func: function (){
Console. log (this. name );
}
};
Var foo = {
Name: 'foobar'
};
Someuser. func (); // output byvoid
Foo. func = someuser. func;
Foo. func (); // output foobar
Name = 'global ';
Func = someuser. func;
Func (); // output global

To prevent func ambiguity, copy another one and get the same result.

Var someuser = {
Name: 'byvoid ',
Func: function (){
Console. log (this. name );
}
};
Var foo = {
Name: 'foobar'
};
Someuser. func (); // output byvoid
Foo. func = someuser. func;
Foo. func (); // output foobar
Name = 'global ';
Var func1 = someuser. func;
Func1 (); // output global

Take a closer look at the example above,When different references are used to call the same function, this pointer will always be the reference
Use the object to which it belongs. In the previous chapter, we mentioned that the function scope of JavaScript is static, that is, 1.
The visible range of functions can be determined in pre-compiled syntax analysis, while the context object can be viewed as static
Scope supplement.

1. call and apply
In JavaScript, call and apply are two magical methods, but they are also confusing.
Methods, and even many people who have experience with JavaScript do not know how to use them.The call and apply functions are as follows:
Call a function using different objects as context
. In short, one object is allowed to call another object.
. At first glance, it seems incredible and confusing, but JavaScript is not strictly

The so-called "member function" concept is displayed only when the relationship between the function and the object is called. Flexible use of call and
Apply can save a lot of time, as we can see later,Call can be used to inherit objects..
The call and apply functions are the same. The slight difference between the two is thatCall uses a parameter table to accept the called letter
Number of parameters, while apply uses an array to accept the parameters of the called Function
.

The call and apply syntaxes are as follows:
Func. call (thisArg [, arg1 [, arg2 [,...])
Func. apply (thisArg [, argsArray])
Among them, func is the reference of the function, thisArg is the context object when func is called, arg1, arg2, or
ArgsArray is the input func parameter. The following code is used as an example to describe the call mechanism:
Var someuser = {
Name: 'byvoid ',
Display: function (words ){
Console. log (this. name + 'says '+ words );
}
};
Var foo = {
Name: 'foobar'
};
Someuser. display. call (foo, 'Hello'); // output foobar says hello
Run this code using Node. js. We can see that foobar is output on the console. Someuser. display is
The called function changes the context to the foo object through call. Therefore, you can access this. name in the function body.
Actually, the access is foo. name, so foobar is output.
2. bind
How can I change the context of the called function? As mentioned above, the call or apply method can be used,
It is inconvenient to use, because the context object must be passed as a parameter each time, and the code is not intuitive. Needle
In this case, we can use the bind method to permanently bind the context of the function so that no matter who calls it
The following are fixed. The bind syntax is as follows:
Func. bind (thisArg [, arg1 [, arg2 [,...])
Among them, func is the function to be bound, thisArg is the changed context object, and arg1 and arg2 are the bound parameters.
Number table. The Return Value of the bind method is the func whose context is thisArg.
. The following example can help you understand bind.
Usage:

Var someuser = {
Name: 'byvoid ',
Func: function (){
Console. log (this. name );
}
};
Var foo = {
Name: 'foobar'
};
Foo. func = someuser. func;
Foo. func (); // output foobar
Foo. func1 = someuser. func. bind (someuser );
Foo. func1 (); // output byvoid
Func = someuser. func. bind (foo );
Func (); // output foobar
Func2 = func;
Func2 (); // output foobar

The code above directly assigns foo. func to someuser. func. When foo. func () is called, this indicates
The needle is foo, so the output result is foobar. Foo. func1 uses the bind method and uses someuser
Bind this pointer to someuser. func. When foo. func1 () is called, this pointer is someuser,
Therefore, the output result is byvoid. (Undefined variables with direct value assignment) The global function func also uses the bind method and uses foo as
The needle is bound to someuser. func. When func () is called, The this pointer is foo, so the output result is foobar.
Func2 directly assigns a value to the bound func, which has the same behavior as func.
3. bind a parameter table
The bind method also has an important function: bind a parameter table, as shown in the following example.
Var person = {
Name: 'byvoid ',
Says: function (act, obj ){
Console. log (this. name + ''+ act +'' + obj );
}
};
Person. says ('loves ', 'diovyb'); // output byvoid loves diovyb
ByvoidLoves = person. says. bind (person, 'loves ');
ByvoidLoves ('you'); // output byvoid loves you
We can see that byvoidLoves binds this pointer to person and binds the first parameter
After calling byvoidLoves, you only need to input the third parameter. This feature can be used to create
A function's "shortcut", and then we can use this "shortcut" to call it, so that deduplication is omitted when multiple code calls are performed.
Enter the same parameters.

4. Understand bind
Despite the beauty of the bind, there are some confusing points, such as the following code:
Var someuser = {
Name: 'byvoid ',
Func: function (){
Console. log (this. name );
}
};
Var foo = {
Name: 'foobar'
};
Func = someuser. func. bind (foo );
Func (); // output foobar
Func2 = func. bind (someuser );
Func2 (); // output foobar


The global function func binds this pointer to foo through someuser. func. bind and calls func () to input
Out of foobar. We try to assign the value of func2 to the bound func and bind this pointer
The result of someuser, but when calling func2, the output value is still foobar, that is, the this pointer is still stuck in foo.
Object. Why? To explain this phenomenon, we must understand the principles of the bind method.
Let's take a look at the simplified version of the bind method (the parameter table cannot be bound ):
Someuser. func. bind = function (self ){
Return this. call (self );
};
Assuming that the above function is the implementation of the bind method of someuser. func, this points
Someuser. func, because the function is also an object, so this. call (self) is used
This pointer calls someuser. func.
// Expand func = someuser. func. bind (foo:
Func = function (){
Return someuser. func. call (foo );
};
// Expand func2 = func. bind (someuser:
Func2 = function (){
Return func. call (someuser );
};
From the above process, we can see that,Func2 actually uses someuser as func. this indicates
The needle calls func, and func does not use this pointer at all, so bind twice is ineffective.
.


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.