Document directory
To create a scope chain, this keyword is provided for every JavaScript code execution context. In its most common usage,this
Serves as an identity function, providing our neighborhoods a way
Referring to themselves. We can't always rely on that behavior,
However: Depending on how we get into a particle neighborhood,this
Might mean something else entirely. In fact,How we get into the neighborhoodIs itself exactly whatthis
Generally refers to. Pay attention to four special situations:
- Calling an Object's Method
In typical object-oriented programming, we need a way to point to and reference the objects we call.this
Serves the purpose admirably, providing our objects the ability to examine themselves, and point at their own properties.
var deep_thought = {<br /> the_answer: 42,<br /> ask_question: function () {<br /> return this.the_answer;<br /> }<br /> };</p><p> var the_meaning = deep_thought.ask_question();
This example builds an object nameddeep_thought
, Sets itsthe_answer
Property to 42, and createsask_question
Method. Whendeep_thought.ask_question()
Is executed, JavaScript establishes an execution context for the function call, settingthis
To the object referenced by whatever came before the last ".", in this case:deep_thought
. The method can then look in the mirrorthis
To examine its own properties, returning the value stored inthis.the_answer
: 42.
- Constructor
Likewise, when defining a function to be used as a constructor withnew
Keyword,this
Can be used to refer to the object being created. Let's rewrite the example abve to reflect that scenario:
<mce:script type="text/javascript"><!--<br /> function BigComputer(answer) {<br /> this.the_answer = answer;<br /> this.ask_question = function () {<br /> return this.the_answer;<br /> }<br /> }</p><p> var deep_thought = new BigComputer(42);<br /> var the_meaning = deep_thought.ask_question();</p><p>// --></mce:script>
Instead of explicitly creatingdeep_thought
Object, we'll write a function to createBigComputer
Objects, andInstantiate deep_thought
As an instance variable vianew
Keyword. Whennew BigComputer()
Is executed, a completely new object is created transparently in the background.BigComputer
Is called, and itsthis
Keyword is set to reference that new object. The function can set properties and methods onthis
, Which is transparently returned at the endBigComputer
'S execution.
Notice, though, thatdeep_thought.the_question()
Still works just as it did before. What's going on there? Why doesthis
Mean something different insidethe_question
Than it does insideBigComputer
? Put simply, weEntered BigComputer
Vianew
, Sothis
Meant "the new object." On the other hand, weEntered the_question
Viadeep_thought
, So while we're re executing that method,this
Means "whateverdeep_thought
Refers ".this
Is not read from the scope chain as other variables are, but instead isResetOn a context by context basis.
- Function Call
What if we just call a normal, everyday function without any of this fancy object stuff? What doesthis
Mean in that scenario?<Mce: script type = "text/javascript"> <! -- <Br/> function test_this () {<br/> return this; <br/>}< br/> var I _wonder_what_this_is = test_this (); </p> <p> // --> </mce: script>
In this case, we weren't provided a contextnew
, Nor were we given a context in the form of an object to piggyback off of. Here,this
Defaults to reference the most global thing it can: for web pages, this iswindow
Object.
- Event Handler
For a more complicated twist on the normal function call, let's say that we're using a function to handleonclick
Event. What doesthis
Mean when the event triggers our function's execution? Unfortunately, there's not a simple answer to this question.
If we write the event handler inline,this
Refers to the globalwindow
Object:
<mce:script type="text/javascript"><!--<br /> function click_handler() {<br /> alert(this); // alerts the window object<br /> }</p><p>// --></mce:script>
...
<button id='thebutton' onclick='click_handler()'>Click me!</button>
However, when we add an event handler via JavaScript,this
Refers to the DOM element that generated the event. (Note: The event
Handling shown here is short and readable, but otherwise poor. Please
Use a real addEvent function instead .):
<script type="text/javascript">
<mce:script type="text/javascript"><!--<br /> function click_handler() {<br /> alert(this); // alerts the button DOM node<br /> }</p><p> function addhandler() {<br /> document.getElementById('thebutton').onclick = click_handler;<br /> }</p><p> window.onload = addhandler;</p><p>// --></mce:script>
...
<button id='thebutton'>Click me!</button>
Complications
Let's run with that last example for a moment longer. What if instead of runningclick_handler
, We wanted to askdeep_thought
A question every time we clicked the button? The code for that seems pretty straightforward; we might try this:
<mce:script type="text/javascript"><!--<br /> function BigComputer(answer) {<br /> this.the_answer = answer;<br /> this.ask_question = function () {<br /> alert(this.the_answer);<br /> }<br /> }</p><p> function addhandler() {<br /> var deep_thought = new BigComputer(42),<br /> the_button = document.getElementById('thebutton');</p><p> the_button.onclick = deep_thought.ask_question;<br /> }</p><p> window.onload = addhandler;<br />// --></mce:script>
In the above Code, we expect to click the button,Deep_thought.ask_question is executed. We get the returned result.
"42." But why is the opposite?Undefined? Where is the error?
?
The problem is simply this: We 've passed off a reference toask_question
Method, which, when executed as an event handler, runs in a different
Context than when it's executed as an object method. In short, the this keyword in ask_question directs to the DOM element node that generates the event, insteadBigComputer object
. DOM element node does not existThe_answer attribute, so the returned result is
Undefined instead
"42 ."setTimeout
Exhibits similar behavior, delaying the execution of a function while at the same time moving it out into a global context.
This issue crops up all over the place in our programs, and it's
Terribly difficult problem to debug without keeping careful track
What's going on in all the corners of your program, especially if your
Object has properties thatDoExist on DOM elements orwindow
Object.
Manipulating Context
.apply()
And
.call()
We reallyDoWant to be able to askdeep_thought
A question when we click the button, and more generally, weDoWant to be able to call object methods in their native context when responding to things like events andsetTimeout
CILS. Two little-known JavaScript methods,apply
Andcall
, Indirectly enable this functionality by allowing us to manually override the default valuethis
When we execute a function call. Let's lookcall
First:
<mce:script type="text/javascript"><!--<br /> var first_object = {<br /> num: 42<br /> };<br /> var second_object = {<br /> num: 24<br /> };</p><p> function multiply(mult) {<br /> return this.num * mult;<br /> }</p><p> multiply.call(first_object, 5); // returns 42 * 5<br /> multiply.call(second_object, 5); // returns 24 * 5<br />// --></mce:script>
In this example, we first define two objects,first_object
Andsecond_object
, Each withnum
Property. Then we definemultiply
Function that accepts a single argument, and returns the product of that argument, andnum
Property of itsthis
Object. If we called that function by itself, the answer returned wowould almost certainly beundefined
, Since the globalwindow
Object doesn' t havenum
Property unless we explicitly set one. We need some way of tellingmultiply
What itsthis
Keyword ought refer to;call
Method ofmultiply
Function is exactly what we're re looking.
The first parameter of the call method defines the direction and object of the this keyword in the execution context of the called method. The remaining parameter of the call method is the parameter of the called method. Therefore, when
Multiply. call (first_object, 5) is executed,
The multiply function is called.
,5
Is the first parameter of the input method,this
RunThe first_object object.
Likewise, whenmultiply.call(second_object, 5)
Is executed,multiply
Function is called,5
Is passed in as the first argument, andthis
Keyword is set to refer to objectsecond_object
.
Apply method and
The call methods are basically the same.
But you can pass parameters to the called function in the form of arrays,
Which can be quite useful when programatically generating function
CILS. Replicating the functionality we just talked about usingapply
Is trivial:
<mce:script type="text/javascript"><!--<br /> ...</p><p> multiply.apply(first_object, [5]); // returns 42 * 5<br /> multiply.apply(second_object, [5]); // returns 24 * 5<br />// --></mce:script>
apply
Andcall
Are very useful on their
Own, and well worth keeping around und in your toolkit, but they only get
Us halfway to solving the problem of context shifts for event handlers.
It's easy to think that we cocould solve the problem by simply usingcall
To shift the meaningthis
When we set up the handler:
function addhandler() {<br /> var deep_thought = new BigComputer(42),<br /> the_button = document.getElementById('thebutton');</p><p> the_button.onclick = deep_thought.ask_question.call(deep_thought);<br />}
The above code still has problems:Call is to execute the function immediately, so we provide
onclick
Handler is the execution result of the function rather than the function itself. We need another feature of JavaScript to solve this problem: bind method.
The Beauty
.bind()
I'm notHugeFan of the Prototype JavaScript framework, but I am very much impressed with the quality of its code as a whole. In particle, one simple addition it makes toFunction
Object has had a hugely positive impact on my ability to manage the context in which function CILS execute:bind
Performs the same general taskcall
, Altering the context in which a function executes. The difference is thatbind
Returns a function reference that can be used later, rather than the result of an immediate execution that we getcall
.
If we simplifybind
Function a bit to get at
Key concepts, we can insert it into the multiplication example we
Discussed earlier to really dig into how it works; it's quite
Elegant solution:
<mce:script type="text/javascript"><!--<br /> var first_object = {<br /> num: 42<br /> };<br /> var second_object = {<br /> num: 24<br /> };</p><p> function multiply(mult) {<br /> return this.num * mult;<br /> }</p><p> Function.prototype.bind = function(obj) {<br /> var method = this,<br /> temp = function() {<br /> return method.apply(obj, arguments);<br /> };</p><p> return temp;<br /> }</p><p> var first_multiply = multiply.bind(first_object);<br /> first_multiply(5); // returns 42 * 5</p><p> var second_multiply = multiply.bind(second_object);<br /> second_multiply(5); // returns 24 * 5<br />// --></mce:script>
First, we definefirst_object
,second_object
, Andmultiply
Function, just as before. With those taken care of, we move on to creatingbind
Method onFunction
Object'sprototype
, Which has the effect of makingbind
Available for all functions in our program. Whenmultiply.bind(first_object)
Is called, JavaScript creates an execution context forbind
Method, settingthis
Tomultiply
Function, and setting the first argument,obj
, To referencefirst_object
. So far, so good.
The real genius of this solution is the creationmethod
, Set equalthis
(multiply
Function itself). When the anonymous function is created on the next line,method
Is accessible via its scope chain, as isobj
(this
Couldn't be used here, because when the newly created function is executed,this
Will be overwritten by a new, local context). This aliasthis
Makes it possible to useapply
To executemultiply
Function, passing inobj
To ensure that the context is set correctly. In computer-science-speak,temp
IsClosureThat, when returned at the end ofbind
Call, can be used in any context whatsoever to executemultiply
In the contextfirst_object
.
This is exactly what we need for the event handler andsetTimeout
Scenarios discussed above. The following code solves that problem completely, bindingdeep_thought.ask_question
Method todeep_thought
Context, so that it executes correctly whenever the event is triggered:
function addhandler() {<br /> var deep_thought = new BigComputer(42),<br /> the_button = document.getElementById('thebutton');</p><p> the_button.onclick = deep_thought.ask_question.bind(deep_thought);<br />}
Beautiful.