There are four different invocation patterns in JavaScript that determine what the hidden parameter this
is bound to.1
Method Invocation Pattern
A method is a function tied to a property on an object. For methods, this
is bound to the object upon invocation. For example:
var person = {
name: 'Calvin',
age: 25,
greet: function () {
alert('My name is ' + this.name + '.');
}
};
person.greet(); //My name is Calvin.
In this example, this
is bound to the person
object upon invoking greet
because greet
is a method of person
.
Function Invocation Pattern
For functions that are not properties on objects, this
is bound to the global object. This is not very intuitive and is often considered one of the ‘bad parts’ of JavaScript–a mistake in language design. Naturally, you’d think this
would be bound to the parent function, and that would have been much more helpful. Regardless, most developers overcome this problem by assigning this
to a variable in the parent function. For example:
// Add a new method to person
person.calculateAge = function (yearsFromNow) {
var self = this;
function yearsOld() {
return self.age + yearsFromNow;
}
alert('I will be ' + yearsOld() + ' years old ' + yearsFromNow + ' years from now.');
}
person.calculateAge(10); //I will be 35 years old 10 years from now.
In this example, I maintain a reference to the context of this
by assigning it to the variable self
. At the time of assignment, this
is bound to the person
object. As a result, I can access the property age
on the person
object from within the yearsOld
function. self
and that
are common names for variables that maintain the context of this
.
What if I had used this
instead of self
?
person.calculateAgeWrong = function (yearsFromNow) {
function yearsOld() {
return this.age + yearsFromNow; //NaN
}
alert('I will be ' + yearsOld() + ' years old ' + yearsFromNow + ' years from now.');
}
person.calculateAgeWrong(10);
Constructor Invocation Pattern
In JavaScript, functions can be invoked with the new
prefix similar to the way objects are constructed in other languages. When this happens, this
is bound to the new object. In addition, the resulting object is created with a link to the hidden prototype
property of the function. This is what makes JavaScript a prototypal inheritance language as opposed to a classical inheritance language. There are no classes, but objects can inherit properties from other objects.
Functions that are designed to be called with the new
prefix are by definition constructors. These functions are distinguished from others by using PascalCase as opposed to camelCase.
var Person = function (name) {
this.name = name;
};
Person.prototype.greet = function () {
return this.name + ' says hi.';
};
alert(new Person('Calvin').greet()); //Calvin says hi.
Notice the greet
function uses this
to access the name
property. this
is bound to Person
.
Apply Invocation Pattern
As a functional object-oriented language, JavaScript makes it possible for functions to have methods as well. The apply
function is a method on the Function.prototype
–the prototype for all JS functions. apply
makes it possible to use one object’s method in the context of another. We can do so by supplying an array with the correct number arguments and and the object to which this will be bound, also known as the context. Therefore, apply
can take two arguments: (1) a context for this
and (2) an array of arguments that will be applied to the method at hand.
var calvin = new Person('Calvin');
var hobbes = {name: 'Hobbes'};
alert(calvin.greet.apply(hobbes)); //Hobbes says hi.
Even though hobbes
does not have a greet
method, we can still apply the greet
method from calvin
because hobbes
has a name
property. If hobbes didn’t have a name
property, the invocation would fail. This example demonstrates the invocation of the apply
function with only one argument. greet
doesn’t have any parameters so we didn’t provide apply
an array of arguments.
Let’s say we have a method greetFriends
that takes two arguments–two objects with a name
property.
Person.prototype.greetFriends = function (friendA, friendB) {
return this.name + ' says hi to ' + friendA.name + ' and ' + friendB.name + '.';
};
var bill = {name: 'Bill Watterson'};
alert(calvin.greetFriends.apply(bill, [calvin, hobbes]));
//Bill says hi to Calvin and Hobbes.
In this example, we supply two arguments to the apply
function: bill
as the context for this
and an array with our two person objects. calvin
and hobbes
become the parameters friendA
and friendB
in greetFriends
.
What if we were to have a method with an unpredictable number of arguments?
Sometimes in a web app you want to make an asynchronous request to a web server, often to a web API to fetch or modify some data. Sometimes you have to make multiple web requests and you don’t know how many because it depends on dynamic user input. Furthermore, you may want to execute some additional code after all the web requests are complete, so you might use a library like jQuery or Q that has a deferral/promise implementation. If you were using jQuery, your code might look something like the following, but with real data and a real purpose, not just to print out numbers:
// example & real world = set variables
var num = Math.ceil(Math.random() * 10),
delay = 0,
deferreds = [],
element = document.getElementById('counter');
// example = print number when deferred is resolved
// real world = make asynchronous GET, POST, PUT, PATCH or DELETE request
function defer() {
delay += 1000;
return $.Deferred(function (def) {
setTimeout(function () {
element.innerHTML = --num;
def.resolve();
}, delay);
}).promise();
}
// example = execute a random number of deferreds
// real world = execute a number of aync requests based on user input
// both = hold promises in an array
for (var i = num; i > 0; i--) {
deferreds.push(defer());
}
// example = start off the countdown
element.innerHTML = num;
// example & real world = apply promises--unknown number until runtime
$.when.apply($, deferreds)
.always(function () {
// example & real world = take action after all promises are resolved
element.innerHTML = num + '...done.';
});
Since this example uses jQuery, I created a JSFiddle you can run to see the result.
References
- The four invocation patterns discussed were taken from JavaScript: The Good Parts by Douglas Crockford. I highly recommend reading this short book if you want to get serious about JavaScript.
More JavaScript
- Block & Function Scope
- Double Equals Comparison Operator
- Logical Operators
- Coercion
- Hoisting
- Closure