If you’re familiar with object-oriented programming, you’re likely accustomed to thinking of functions, methods, and class constructors as three separate things. In JavaScript, these are just three different usage patterns of one single construct: functions.
The simplest usage pattern is the function call:
function hello(username) { return "hello, " + username; } hello("Keyser Söze"); // "hello, Keyser Söze"
This does exactly what it looks like: It calls the hello function and binds the name parameter to its given argument.
Methods in JavaScript are nothing more than object properties that happen to be functions:
var obj = { hello: function() { return "hello, " + this.username; }, username: "Hans Gruber" }; obj.hello(); // "hello, Hans Gruber"
Notice how hello refers to this to access the properties of obj. You might be tempted to assume that this gets bound to obj because the hello method was defined on obj. But we can copy a reference to the same function in another object and get a different answer:
var obj2 = { hello: obj.hello, username: "Boo Radley" }; obj2.hello(); // "hello, Boo Radley"
What really happens in a method call is that the call expression itself
determines the binding of this, also known as the call’s receiver.
The expression obj.hello() looks up the hello property of obj and
calls it with receiver obj. The expression obj2.hello() looks up the
hello property of obj2—which happens to be the same function as
obj.hello—but calls it with receiver obj2. In general, calling a method
on an object looks up the method and then uses the object as the
method’s receiver.
Since methods are nothing more than functions called on a particu-
lar object, there is no reason why an ordinary function can’t refer to
this:
function hello() { return "hello, " + this.username; } This can be useful for predefining a function for sharing among multiple objects: var obj1 = { hello: hello, username: "Gordon Gekko" }; obj1.hello(); // "hello, Gordon Gekko" var obj2 = { hello: hello, username: "Biff Tannen" }; obj2.hello(); // "hello, Biff Tannen"
However, a function that uses this is not particularly useful to call as a function rather than a method:
hello(); // "hello, undefined"
Rather unhelpfully, a nonmethod function call provides the global object as the receiver, which in this case has no property called name and produces undefined. Calling a method as a function rarely does anything useful if the method depends on this, since there is no reason to expect the global object to match the expectations that the method has of the object it is called on. In fact, binding to the global object is a problematic enough default that ES5’s strict mode changes the default binding of this to undefined:
function hello() {
"use strict";
return "hello, " + this.username;
}
hello(); // error: cannot read property "username" of undefined
This helps catch accidental misuse of methods as plain functions by failing more quickly, since attempting to access properties of undefined immediately throws an error.
The third use of functions is as constructors. Just like methods and plain functions, constructors are defined with function:
function User(name, passwordHash) { this.name = name; this.passwordHash = passwordHash; } Invoking User with the new operator treats it as a constructor: var u = new User("sfalken", "0ef33ae791068ec64b502d6cb0191387"); u.name; // "sfalken"
Unlike function calls and method calls, a constructor call passes a brand-new object as the value of this, and implicitly returns the new object as its result. The constructor function’s primary role is to initialize the object.
Things to Remember
? Method calls provide the object in which the method property is looked up as their receiver.
? Function calls provide the global object (or undefined for strict functions) as their receiver. Calling methods with function call syntax is rarely useful.
? Constructors are called with new and receive a fresh object as their receiver.