When a function is created, a keyword called this
is created (behind the scenes), which links to the object in which the function operates. Said another way, this
is available to the scope of its function, yet is a reference to the object of which that functionis a property or method.
Lets take a look at the cody
object from a previous article:
Sample: sample98.html
<!DOCTYPE html><html lang="en"><body><script> var cody = { living: true, age: 23, gender: 'male', getGender: function () { return cody.gender; } }; console.log(cody.getGender()); // Logs 'male'. </script></body></html>
Notice how inside of the getGender
function, we are accessing the gender
property using dot notation (cody.gender
) on the cody
object itself. This can be rewritten using this
to access the cody
object because this
points to the cody
object.
Sample: sample99.html
<!DOCTYPE html><html lang="en"><body><script> var cody = { living: true, age: 23, gender: 'male', getGender: function () { return this.gender; } }; console.log(cody.getGender()); // Logs 'male'. </script></body></html>
The this
used in this.gender
simply refers to the cody object on which the function is operating.
The topic of this
can be confusing, but it does not have to be. Just remember that in general, this
is used inside of functions to refer to the object the function is contained within, as opposed to the function itself (exceptions include using the new
keyword or call()
and apply()
).
The keyword this
looks and acts like any other variable, except you can't modify it.
As opposed to arguments
and any parameters sent to the function, this
is a keyword (not a property) in the call/activation object.
How Is the Value of this
Determined?
The value of this
, passed to all functions, is based on the context in which the function is called at run time. Pay attention here, because this is one of those quirks you just need to memorize.
The myObject
object in the following code sample is given a property called sayFoo, which points to the sayFoo
function. When the sayFoo
function is called from the global scope, this
refers to the window
object. When it is called as a method of myObject, this
refers to myObject
.
Since myObject
has a property named foo
, that property is used.
Sample: sample100.html
<!DOCTYPE html><html lang="en"><body><script> var foo = 'foo'; var myObject = { foo: 'I am myObject.foo' }; var sayFoo = function () { console.log(this['foo']); }; // Give myObject a sayFoo property and have it point to the sayFoo function. myObject.sayFoo = sayFoo; myObject.sayFoo(); // Logs 'I am myObject.foo'. sayFoo(); // Logs 'foo'. </script></body></html>
Clearly, the value of this
is based on the context in which the function is being called. Consider that both myObject.sayFoo
and sayFoo
point to the same function. However, depending upon where (the context) sayFoo()
is called from, the value of this
is different.
If it helps, here is the same code with the head object (window
) explicitly used.
Sample: sample101.html
<!DOCTYPE html><html lang="en"><body><script> window.foo = 'foo'; window.myObject = { foo: 'I am myObject.foo' }; window.sayFoo = function () { console.log(this.foo); }; window.myObject.sayFoo = window.sayFoo; window.myObject.sayFoo(); window.sayFoo(); </script></body></html>
Make sure that as you pass around functions, or have multiple references to a function, you realize that the value of this will change depending upon the context in which you call the function.
All variables except this
and arguments
follow lexical scope
The this
Keyword Refers to the Head Object In Nested Functions
You might be wondering what happens to this
when it is used inside of a function that is contained inside of another function. The bad news is in ECMA 3, this
loses its way and refers to the head object (the window
object in browsers), instead of the object within which the function is defined.
In the following code, this
inside of func2
and func3
loses its way and refers not to myObject
but instead to the head object.
Sample: sample102.html
<!DOCTYPE html><html lang="en"><body><script> var myObject = { func1: function () { console.log(this); // Logs myObject. var func2 = function () { console.log(this) // Logs window, and will do so from this point on. var func3 = function () { console.log(this); // Logs window, as it’s the head object. } (); } (); } } myObject.func1(); </script></body></html>
The good news is that this will be fixed in ECMAScript 5. For now, you should be aware of this predicament, especially when you start passing functions around as values to other functions.
Consider the next sample and what happens when passing an anonymous function to foo.func1
. When the anonymous function is called inside of foo.func1
(a function inside of a function), the this
value inside of the anonymous function will be a reference to the head object.
Sample: sample103.html
<!DOCTYPE html><html lang="en"><body><script> var foo = { func1: function (bar) { bar(); // Logs window, not foo. console.log(this); // The this keyword here will be a reference to the foo object. } } foo.func1(function () { console.log(this) }); </script></body></html>
Now you will never forget: the this
value will always be a reference to the head object when its host function is encapsulated inside of another function or invoked within the context of another function (again, this is fixed in ECMAScript 5).
Working Around the Nested Function Issue by Leveraging the Scope Chain
So that the this
value does not get lost, you can simply use the scope chain to keep a reference to this
in the parent function. The following sample demonstrates how, using a variable called that
, and leveraging its scope, we can keep better track of function context.
Sample: sample104.html
<!DOCTYPE html><html lang="en"><body><script> var myObject = { myProperty: 'I can see the light', myMethod : function(){ var that = this; // Store a reference to this (myObject) in myMethod scope. var helperFunction = function() { // Child function. // Logs 'I can see the light' via scope chain because that = this. console.log(that.myProperty); // Logs 'I can see the light'. console.log(this); // Logs window object, if we don't use "that". }(); } } myObject.myMethod(); // Invoke myMethod. </script></body></html>
Controlling the Value of this
Using call()
or apply()
The value of this
is normally determined from the context in which a function is called (except when the new keyword is usedmore about that in a minute), but you can overwrite and control the value of this
using apply()
or call()
to define what object this
points to when invoking a function. Using these methods is like saying: "Hey, call X function but tell the function to use Z object as the value for this
." By doing so, the default way in which JavaScript determines the value of this
is overridden.
In the next sample, we create an object and a function. We then invoke the function via call()
so that the value of this
inside the function uses myObject
as its context. The statements inside the myFunction
function will then populate myObject
with properties instead of populating the head object. We have altered the object to which this
(inside of myFunction
) refers.
Sample: sample105.html
<!DOCTYPE html><html lang="en"><body><script> var myObject = {}; var myFunction = function (param1, param2) { // Set via call(), 'this' points to myObject when function is invoked. this.foo = param1; this.bar = param2; console.log(this) // Logs Object {foo = 'foo', bar = 'bar'} }; myFunction.call(myObject, 'foo', 'bar'); // Invoke function, set this value to myObject. console.log(myObject) // Logs Object {foo = 'foo', bar = 'bar'} </script></body></html>
In the previous example, we used call()
, but apply()
could be used as well. The difference between the two is how the parameters for the function are passed. Using call()
, the parameters are just comma-separated values. Using apply()
, the parameter values are passed inside of an array as shown in the following sample.
Sample: sample106.html
<!DOCTYPE html><html lang="en"><body><script> var myObject = {}; var myFunction = function (param1, param2) { // Set via apply(), this points to myObject when function is invoked. this.foo = param1; this.bar = param2; console.log(this) // Logs Object {foo = 'foo', bar = 'bar'} }; myFunction.apply(myObject, ['foo', 'bar']); // Invoke function, set this value. console.log(myObject) // Logs Object {foo = 'foo', bar = 'bar'} </script></body></html>
What you need to learn here is that you can override the default way in which JavaScript determines the value of this
in a function's scope.
Using the this
Keyword Inside a User-Defined Constructor Function
When a function is invoked with the new
keyword, the value of this
as its stated in the constructor refers to the instance itself. Said another way: In the constructor function, we can leverage the object via this
before the object is actually created. In this case, the default value of this
changes in a way similar to using call()
or apply()
.
In the following sample, we set up a Person
constructor function that uses this
to reference an object being created. When an instance of Person
is created, this.name
will reference the newly created object and place a property called name in the new object with a value from the parameter (name
) passed to the constructor function.
Sample: sample107.html
<!DOCTYPE html><html lang="en"><body><script> var Person = function (name) { this.name = name || 'john doe'; // this will refer to the instance created. } var cody = new Person('Cody Lindley'); // Create an instance based on the Person constructor. console.log(cody.name); // Logs 'Cody Lindley'. </script></body></html>
Again, this
refers to the "object that is to be" when the constructor function is invoked using the new
keyword. Had we not used the new
keyword, the value of this
would be the context in which Person
is invoked - in this case the head object. Let's examine the following scenario:
Sample: sample108.html
<!DOCTYPE html><html lang="en"><body><script> var Person = function (name) { this.name = name || 'john doe'; } var cody = Person('Cody Lindley'); // Notice we did not use 'new'. console.log(cody.name); // Undefined. The value is actually set at window.name console.log(window.name); // Logs 'Cody Lindley'. </script></body></html>
The Keyword this
Inside a Prototype Method Refers to a Constructor Instance
When used in functions added to a constructors prototype
property, this
refers to the instance on which the method is invoked. Say we have a custom Person()
constructor function. As a parameter, it requires the persons full name. In case we need to access the full name of the person, we add a whatIsMyFullName
method to the Person.prototype
so that all Person
instances inherit the method. When using this
, the method can refer to the instance invoking it (and thus its properties).
Here I demonstrate the creation of two Person
objects (cody
and lisa
) and the inherited whatIsMyFullName
method that contains the this keyword to access the instance.
Sample: sample109.html
<!DOCTYPE html><html lang="en"><body><script> var Person = function (x) { if (x) { this.fullName = x }; }; Person.prototype.whatIsMyFullName = function () { return this.fullName; // 'this' refers to the instance created from Person() } var cody = new Person('cody lindley'); var lisa = new Person('lisa lindley'); // Call the inherited whatIsMyFullName method, which uses this to refer to the instance. console.log(cody.whatIsMyFullName(), lisa.whatIsMyFullName()); /* The prototype chain is still in effect, so if the instance does not have a fullName property, it will look for it in the prototype chain. Next, we add a fullName property to both the Person prototype and the Object prototype. See the notes that follow this sample. */ Object.prototype.fullName = 'John Doe'; var john = new Person(); // No argument is passed so fullName is not added to the instance. console.log(john.whatIsMyFullName()); // Logs 'John Doe'. </script></body></html>
Conclusion
The concept to take away here is that
the keyword this is used to refer to instances when used inside of a method contained in the prototype
object. If the instance does not contain the property, the prototype lookup begins.
If the instance or the object pointed to by this
does not contain the property being referenced, the same rules that apply to any property lookup are applied, and the property will be "looked up" on the prototype chain. So in our example, if the fullName
property was not contained within our instance, fullName
would be looked for at Person.prototype.fullName
, then Object.prototype.fullName
.
Comments