Javascript scope and JS Scope
After reading this article, I found that the more basic things are displayed, the more advanced they are.
Reference: http://realazy.org/blog/2007/07/18/scope-in-javascript/
Scope is one of the cornerstones of the Javascript language. It may also be the most headache for me when building complex programs. I can't remember how many times after passing control between functions, I forgot which object the this keyword references, and even, I often saved the nation in various chaotic ways, I tried to pretend to be a normal code and found the variables to be accessed in my own understanding.
This article will solve this problem positively: Briefly describe the context and scope Definitions, analyze the two methods that allow us to control the context, and finally go deep into an efficient solution, it can effectively solve 90% of the problems I encountered.
Where am I? Who are you?
Every byte of the Javascript program is executed in this or that running context. You can think of the context as the neighbor of the Code. They can specify the source of each line of code, and the friends and neighbors. Yes, this is very important information, because JavaScript society has strict rules that define who can interact. The running context is a small community with a door to guard, rather than a small door open to it.
We can usually refer to these social boundaries as scopes, and there is ample importance for legislation in the charter of every neighbor, this Charter is the scope chain of context ). In a specific neighborhood relationship, the code can only access the variables in its scope chain. Compared with variables beyond its neighbors, the Code prefers to deal with local variables.
Specifically, executing a function will create a different running context, which will add the local scope to the scope Chain Defined by it. Javascript parses the identifier in a specific context by means of a local climbing of the scope chain. This indicates that the level variable takes precedence over the variable with the same name at the upper level of the scope chain. Obviously, when my friends talk about "Mike West" (the original author of this article), they talk about me, not bluegrass singer or Duke worker SOR, though) the latter two are much more famous.
Let's look at some examples to explore these meanings:
<SCRIPT type = "text/JavaScript">
VaR ima_celebrity = "everyone can see me! I'm famous! ",
The_president = "I'm the decider! ";
Function Pleasantville (){
VaR the_mayor = "I rule Pleasantville with an iron fist! ",
Ima_celebrity = "all my neighbors know who I am! ";
Function lonely_house (){
VaR agoraphobic = "I fear the day star! ",
A_cat = "meow .";
}
}
</SCRIPT>
Our all-star, ima_celebrity, is a household name (everyone knows her ). She is politically active and dares to yell at the President (the_president) at a very frequent grassroots level ). She will sign and answer questions for every person she encounters. That is to say, she will not have private contact with her fans. She knows that fans exist and have their own personal lives to some extent, but she does not know what fans are doing or even the names of fans.
In Pleasantville, the mayor (the_mayor) is well known. She often walks in her town, chatting with her voters, shaking hands, and kissing children. Because Pleasantville is a relatively large and important neighbor, the mayor put a red phone in her office. It is a 7 × 24 hotline that can pass through the president. She can also see lonely_house on the outskirts of the city, but she never cares who lives in it.
Lonely_house is a world of self. Fear patients often seek out cards and feed a kitten (a_cat ). He occasionally calls the mayor (the_mayor) to ask for some local noise control, and even writes some fan words to her after seeing ima_celebrity in the local news (of course, this is the ima_celebrity in Pleasantville ).
This? Is that Xiami?
In addition to setting up a scope chain, each running context also provides a keyword named this. Its common usage is that this, as a unique feature, provides neighbors with a way to access it. But always relying on this behavior is not reliable: It depends on how we enter the specific circumstances of a particular neighbor, this represents everything else. In fact, how we go into the neighbor's house is usually referred to by this. There are four situations worth special attention:
Call object Method
In classical object-oriented programming, we need to identify and reference the current object. This plays this role well, providing our objects with the ability to find themselves and pointing to their own attributes.
<SCRIPT type = "text/JavaScript">
VaR deep_thought = {
The_answer: 42,
Ask_question: function (){
Return this. the_answer;
}
};
VaR the_meaning = deep_thought.ask_question ();
</SCRIPT>
In this example, an object named deep_thought is created, its attribute the_answer is set to 42, and a method named ask_question is created ). When deep_thought.ask_question () is executed, JavaScript creates a runtime context for the function call and points this to the referenced object through the "." operator. This is the deep_thought object. Then this method can find its own attributes in the mirror through this, and return the value saved in this. the_answer: 42.
Constructor
Similarly, when defining a function that uses the New Keyword as the constructor, this can be used to reference the newly created object. Let's rewrite an example that reflects this situation:
<SCRIPT type = "text/JavaScript">
Function bigcomputer (answer ){
This. the_answer = answer;
This. ask_question = function (){
Return this. the_answer;
}
}
VaR deep_thought = new bigcomputer (42 );
VaR the_meaning = deep_thought.ask_question ();
</SCRIPT>
We compile a function to create a bigcomputer object, instead of simply creating a deep_thought object, and instantiate deep_thought as an instance variable using the new keyword. When new bigcomputer () is executed, a new object is created transparently in the background. When bigcomputer is called, its this keyword is set to reference the new object. This function can set attributes and methods on this, and eventually it will return transparently after bigcomputer execution.
Even so, it should be noted that the deep_thought.the_question () can still be executed as before. So what happened here? Why is this different between the_question and bigcomputer? In short, we use new to enter bigcomputer, so this indicates "New (new) object ". On the other hand, we enter the_question through deep_thought, so when we execute this method, this indicates "the object referenced by deep_thought ". This is not read from the scope chain like other variables, but reset in context based on context.
Function call
If there is no such thing as an object, we call a common function. In this case, what does this mean?
<SCRIPT type = "text/JavaScript">
Function test_this (){
Return this;
}
VaR I _w
Onder_what_this_is = test_this ();
</SCRIPT>
In such cases, we do not provide context through new, nor secretly provide context in some object form. Here, this tries to reference the most global thing by default: For a webpage, this is a window object.
Event processing functions
Calls are more complex than calls of common functions. Assume that we use a function to handle an onclick event. When an event triggers the running of our function, what does this represent? Unfortunately, there will be no simple answer to this question.
If we write an inline event handler, This references a global window object:
<SCRIPT type = "text/JavaScript">
Function click_handler (){
Alert (this); // The Window object is displayed.
}
</SCRIPT>
...
<Button id = 'thebutton 'onclick = 'click _ handler () '> click me! </Button>
However, if we use JavaScript to add an event handler, This references the DOM element that generates the event. (Note: The event processing here is very simple and easy to read, but the others are quite different. Use the real addevent function instead ):
<SCRIPT type = "text/JavaScript">
Function click_handler (){
Alert (this); // The Dom node of the pop-up button
}
Function addhandler (){
Document. getelementbyid ('thebutton'). onclick = click_handler;
}
Window. onload = addhandler;
</SCRIPT>
...
<Button id = 'thebutton'> click me! </Button>
Complexity
Let's briefly run this final example. We need to ask the deep_thought question. If we don't directly run click_handler but click the button, what will happen? The code to solve this problem seems very direct, and we may do this:
<SCRIPT type = "text/JavaScript">
Function bigcomputer (answer ){
This. the_answer = answer;
This. ask_question = function (){
Alert (this. the_answer );
}
}
Function addhandler (){
VaR deep_thought = new bigcomputer (42 ),
The_button = Document. getelementbyid ('thebutton ');
The_button.onclick = deep_thought.ask_question;
}
Window. onload = addhandler;
</SCRIPT>
Perfect, right? Imagine that when we click the button, deep_thought.ask_question is executed and we get "42 ". But why does the browser give us an undefined? Where are our mistakes?
In fact, the problem is obvious: we pass a reference to ask_question, which is executed as an event processing function, which is not the same as the context for running as an object method. In short, the this keyword in ask_question points to the DOM element that generates the event, rather than in the bigcomputer object. The Dom element does not have a_answer attribute, so we get undefined instead of "42 ″. setTimeout also has similar behaviors. It delays function execution and runs to a global context.
This problem occurs from time to time in all corners of the program. If you do not track every corner of the program in detail, it is still very difficult to troubleshoot, especially when your object has an attribute with the same name as a DOM element or window object.
Use. Apply () and. Call () to control the context
When we click the button, what we really need is to be able to ask the deep_thought question. Further, what we really need is to respond to the event and setTimeout call, methods that call objects in their original context. There are two little-known Javascript methods: Apply and call. When we execute a function call, we can save the country by curve to help us achieve our goal and allow us to manually override the default value of this. Let's first look at call:
<SCRIPT type = "text/JavaScript">
VaR first_object = {num: 42 };
VaR second_object = {num: 24 };
Function multiply (mult ){
Return this. Num * mult;
}
Multiply. Call (first_object, 5); // 42*5 is returned.
Multiply. Call (second_object, 5); // return 24*5
</SCRIPT>
In this example, we first define two objects, first_object and second_object, which have their own num attributes. Then a multiply function is defined, which accepts only one parameter and returns the product of the parameter and the num attribute of the object referred to by this. If we call the function itself, the returned answer is probably undefined, because the global window object does not have a num attribute unless explicitly specified. We need some ways to tell Multiply what the this keyword should reference. The call method of multiply is exactly what we need.
The first parameter of call defines the object referred to by this in the executed function. Other parameters are passed into the executed function, just like the call of the function itself. Therefore, when multiply. Call (first_object, 5) is executed, multiply is called, 5 is passed as the first parameter, and this keyword is set as a reference of first_object. Similarly, when multiply. Call (second_object, 5) is executed, 5 is passed as the first parameter, and this keyword is set as a reference of second_object.
Apply works in the same way as call, but it allows you to wrap parameters into an array and pass them to the call function. This is especially useful when you generate function calls in a program. When you use apply to reproduce the previous code, the difference is not big:
<SCRIPT type = "text/JavaScript">
...
Multiply. Apply (first_object, [5]); // 42*5 is returned.
Multiply. Apply (second_object, [5]); // returns 24*5
</SCRIPT>
Both apply and call are very useful and worth storing in your toolbox. However, for the context problems changed by event processing functions, they are just sending Buddha to westday, we have to solve the problem. When constructing a processing function, we naturally think that we only need to simply use call to change the meaning of this:
Function addhandler (){
VaR deep_thought = new bigcomputer (42 ),
The_button = Document. getelementbyid ('thebutton ');
The_button.onclick = deep_thought.ask_question.call (deep_thought );
}
The reason why the Code has a problem is simple: Call immediately executes the function (in fact, it can be encapsulated using an anonymous function, such as the_button.onclick = function () {deep_thought.ask_question.call (deep_thought );}, but it is not elegant enough than the bind to be discussed ). We will give the onclcik processing function a result after the function is executed, rather than the reference of the function. So we need to use another JavaScript feature to solve this problem.
. The beauty of BIND ()
I'm not a loyal fan of prototype JavaScript framework, but I'm impressed with its overall code quality. Specifically, it adds a concise supplement to the function object and has a great positive impact on the context after I manage the call execution of the function: bind performs the same common tasks as call, change the context of function execution. The difference is that the BIND returns the final result that the function reference can be used for backup, rather than the immediate execution of call.
If you need to simplify the BIND function to grasp the key of the concept, we can first insert it into the product example discussed above to see how it actually works. This is a pretty elegant solution:
<SCRIPT type = "text/JavaScript">
VaR first_object = {num: 42 };
VaR second_object = {num: 24 };
Function multiply (mult ){
Return this. Num * mult;
}
Function. Prototype. Bind = function (OBJ ){
VaR method = This,
Temp = function (){
Return method. Apply (OBJ, arguments );
};
Return temp;
}
VaR first_multiply = multiply. BIND (first_object );
First_multiply (5); // 42*5 is returned.
VaR second_multiply = multiply. BIND (second_object );
Second_multiply (5); // returns 24*5
</SCRIPT>
First, we define first_object, second_object, and multiply functions. After careful processing, we continue to define a bind method for the prototype of the function object. In this way, all functions in our program have a bind method available. When multiply. BIND (first_object) is executed, JavaScript creates a runtime context for the bind method, sets this as a reference to the multiply function, and sets the first parameter OBJ as a reference of first_object. So far, everything is smooth.
The real genius of this solution lies in the creation of method, which is referred to by this reference (that is, the multiply function itself ). The anonymous function of the current row is created, and the method is accessed through its scope chain, and obj is also (do not use this here, because after the newly created function is executed, this will be overwritten by a new local context ). This alias makes it possible to execute the multiply function by applying, while passing OBJ ensures that the context is correct. In computer science, temp is a closure. It can ensure that multiply is executed in the context of first_object, and the final return of the BIND call can be used in any context.
This is what is really needed in the event processing function and setTimeout situations mentioned above. The following code completely solves these problems. Bind The deep_thought.ask_question Method to the context of deep_thought, so that it can run correctly when any event is triggered:
Function addhandler (){
VaR deep_thought = new bigcomputer (42 ),
The_button = Document. getelementbyid ('thebutton ');
The_button.onclick = deep_thought.ask_question.bind (deep_thought );
}