this
is a special word in JavaScript that is important whenever you are thinking in object-oriented terms.
The value of this
inside a function, effectively depends on the object which called it. The ECMAScript Language Specification says, “The this keyword evaluates to the value of the ThisBinding of the current execution context.”
The use of the “this” keyword inside a function should be familiar to the C++/C# developers among us—it refers to the object through which the method is called ( developers who use Visual Basic should find it familiar, too—it’s called “Me” in Visual Basic).
This post borrows heavily from Daniel Trebbien’s response from JavaScript “this” keyword and some added examples from Ray Djajadinata’s article in MSDN Magazine, Create Advanced Web Applications With Object-Oriented Techniques.
The Value of this Depends on Context
We talked about scope in a previous posting in this series. Scope is when a variable name or other identifier is valid and can be used. Outside the scope of a variable name, the variable’s value may still be stored, and is not accessible in some way.
In JavaScript, when you define a variable inside a function, the variable is accessible inside that function. Any variable not inside a function is in the global scope and accessible everywhere in your code.
So what does that mean to the this keyword?
The ECMAScript Standard defines this
as a keyword that “evaluates to the value of the ThisBinding of the current execution context”. ThisBinding is something that the JavaScript interpreter maintains as it evaluates JavaScript code, like a special CPU register which holds a reference to an object. The interpreter updates the ThisBinding whenever establishing an execution context in one of only three different cases:
Global Context
This is the case for JavaScript code that is evaluated when a <script>
element is encountered:
https://gist.github.com/devdays/2fa1697fa84f4ab43fe7<
When evaluating code in the initial global execution context, ThisBinding is set to the global object, window.
Entering eval code
The ThisBinding is updated
-
… by a direct call to eval()
ThisBinding is left unchanged; it is the same value as the ThisBinding of the calling execution context.
-
… if not by a direct call to eval()
ThisBinding is set to the global object as if executing in the initial global execution context.
§15.1.2.1.1 defines what a direct call to eval() is. Basically, eval(...)
is a direct call whereas something like (0, eval)(...)
or var indirectEval = eval; indirectEval(...);
is an indirect call to eval(). See chuckj’s answer to (1,eval)(‘this’) vs eval(‘this’) in JavaScript? and this blog post by Dmitry Soshnikov for when you might use an indirect eval() call.
Inside a Function
We can say that every JavaScript function has a hidden argument named this.
always refers to the “owner” of the function we’re executing, or rather, to the object that a function is a method of.this
For example:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function makeMeRed() { | |
var docBody = this.document.body; | |
docBody.style.background = '#cc0000'; | |
} |
In the function makeMeRed(), the owner is the page so this returns the window object.
If a function is called on an object, such as in obj.myMethod()
or the equivalent obj["myMethod"]()
, then ThisBinding is set to the object. In most other cases, ThisBinding is set to the global object
The reason for writing “in most other cases” is because there are eight ECMAScript 5 built-in functions that allow ThisBinding to be specified in the arguments list. These special functions take a so-called thisArg which becomes the ThisBinding when calling the function.
These special built-in functions are:
- Function.prototype.apply( thisArg, argArray )
- Function.prototype.call( thisArg [ , arg1 [ , arg2, … ] ] )
- Function.prototype.bind( thisArg [ , arg1 [ , arg2, … ] ] )
- Array.prototype.every( callbackfn [ , thisArg ] )
- Array.prototype.some( callbackfn [ , thisArg ] )
- Array.prototype.forEach( callbackfn [ , thisArg ] )
- Array.prototype.map( callbackfn [ , thisArg ] )
- Array.prototype.filter( callbackfn [ , thisArg ] )
In the case of the Function.prototype functions, they are called on a function object, but rather than setting ThisBinding to the function object, ThisBinding is set to the thisArg.
In the case of the Array.prototype functions, the given callbackfn is called in an execution context where ThisBinding is set to thisArg if supplied; otherwise, to the global object.
Using the ‘new’ operator
When constructing a new object via the new
operator, the JavaScript interpreter creates a new, empty object, sets some internal properties, and then calls the constructor function on the new object. Thus, when a function is called in a constructor context, the value of this
is the new object that the interpreter created:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function MyType() { | |
this.someData = "a string"; | |
} | |
var instance = new MyType(); | |
// Kind of like the following, but there are more steps involved: | |
// var instance = {}; | |
// MyType.call(instance); |
Inside jQuery and Your own JavaScript libraries
Those are the rules for plain JavaScript. When you begin using JavaScript libraries (e.g. jQuery), you may find that certain library functions manipulate the value of this
. The developers of those JavaScript libraries do this because it tends to support the most common use cases, and users of the library typically find this behavior to be more convenient. When passing callback functions referencing this
to library functions, you should refer to the documentation for any guarantees about what the value of this
is when the function is called.
If you are wondering how a JavaScript library manipulates the value of this
, the library is simply using one of the built-in JavaScript functions accepting a thisArg. You, too, can write your own function taking a callback function and thisArg:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function doWork(callbackfn, thisArg) { | |
//… | |
if (callbackfn != null) callbackfn.call(thisArg); | |
} |
Example
To summarize, every function object has a method named call, which calls the function as a method of the first argument. That is, whichever object we pass into call as its first argument will become the value of “this” in the function invocation.
Here’s an example of using this and showing how it changes as the object changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function displayQuote() { | |
// the value of "this" will change; depends on | |
// which object it is called through | |
console.log(this.memorableQuote); | |
} | |
var williamShakespeare = { | |
"memorableQuote": "It is a wise father that knows his own child.", | |
"sayIt" : displayQuote | |
}; | |
var markTwain = { | |
"memorableQuote": "Golf is a good walk spoiled.", | |
"sayIt" : displayQuote | |
}; | |
var oscarWilde = { | |
"memorableQuote": "True friends stab you in the front." | |
// we can call the function displayQuote | |
// as a method of oscarWilde without assigning it | |
// as oscarWilde’s method. | |
//"sayIt" : displayQuote | |
}; | |
williamShakespeare.sayIt(); // true, true | |
markTwain.sayIt(); // he didn’t know where to play golf | |
// watch this, each function has a method call() | |
// that allows the function to be called as a | |
// method of the object passed to call() as an | |
// argument. | |
// this line below is equivalent to assigning | |
// displayQuote to sayIt, and calling oscarWilde.sayIt(). | |
displayQuote.call(oscarWilde); // ouch! |
The output is
It is a wise father that knows his own child. Golf is a good walk spoiled. True friends stab you in the front.
You can try it here: http://jsfiddle.net/w0ve2r04/1/
Best Practice
One thing to remember is never to call functions that contain “this” without an owning object. If you do, you will be trampling over the global namespace, because in that call, “this” will refer to the Global object, and that can really wreak havoc in your application. For example, below is a script that changes the behavior of JavaScript’s global function isNaN. Definitely not recommended
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
alert("NaN is NaN: " + isNaN(NaN)); | |
function x() { | |
this.isNaN = function() { | |
return "not anymore!"; | |
}; | |
} | |
// alert!!! trampling the Global object!!! | |
x(); | |
alert("NaN is NaN: " + isNaN(NaN)); |
Inside an Event
Inside an event, the owner is the HTML element the event belongs to. For example, in an onclick event, the owner is the element that fired the event.
In an event handler for an element, with default capturing (false), this
will refer to the element which detected the event.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
element.addEventListener('keydown', function (event) { | |
// `this` will point to `element` | |
}, false); |
When capturing an event (true), say at the window level, event.target
, will refer to the element which originated the event, while this
will refer to the capturing element. For example:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
window.addEventListener("error", function (event) { | |
event.target.src = 'some_path'; | |
// `this` will point to window | |
// `event.target` will point to the element that had an error | |
}, true); |
Call and Apply and Bind
You can use call(),
apply()
and bind()
when you want that function to later be called with a certain context.
Use call(),
apply() to
call the function immediately, or use bind()
to return a function that when later executed will have the correct context set for calling the original function. This way you can maintain context in async callbacks, and events.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function sayHello(){ | |
alert(this.message); | |
} | |
var obj = { | |
message : "hello" | |
}; | |
setTimeout(function(){sayHello.call(obj)}, 1000); |
is the equilivant of:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function sayHello(){ | |
alert(this.message); | |
} | |
var obj = { | |
message : "hello" | |
}; | |
setTimeout(sayHello.bind(obj), 1000); |
Function.prototype inherits this
Where a function uses the this
keyword in its body, its value can be bound to a particular object during execution using the call()
or apply()
methods that all functions inherit from Function.prototype.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function MyObject(element) { | |
this.elm = element; | |
element.addEventListener('click', this.onClick.bind(this), false); | |
}; | |
MyObject.prototype.onClick = function(e) { | |
var dr;g = this; //do something with [self]… | |
//without bind the context of this function wouldn't be a MyObject | |
//instance as you would normally expect. | |
}; |
When you want async callbacks and pass a member method for, but still want the context to be the instance that started the async action, you can use a simple naïve implementation of bind:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Function.prototype.bind = function(ctx) { | |
var fn = this; | |
return function() { | |
fn.apply(ctx, arguments); | |
}; | |
}; |
Take the Quiz
Take the quiz offered by http://stackoverflow.com/questions/3127429/javascript-this-keyword
References
Create Advanced Web Applications With Object-Oriented Techniques