The 4 Invocation Patterns in JavaScript
Every function in JavaScript receives two extra parameters: this and arguments. The value of this is determined entirely by how the function is invoked. There are four patterns.
Invoking a function in JavaScript does more than execute its code. According to Douglas Crockford’s JavaScript: The Good Parts, every function receives two additional parameters beyond those declared: this and arguments. The value of this is not fixed. It is determined entirely by how the function is invoked. There are four invocation patterns in JavaScript, and each one binds this differently.
1. Function Invocation Pattern
A function is invoked directly, not as a property of an object.
function add(a, b) {
return a + b;
}
add(2, 3); // 5
In this pattern, this is bound to the global object, which is window in a browser. In strict mode, this is undefined. This behaviour is considered a design flaw in the language. A common workaround when a nested function needs access to the outer this is to capture it in a variable by convention named self or that.
const obj = {
value: 10,
getValue: function() {
const self = this;
function inner() {
return self.value; // this inside inner() would be global
}
return inner();
}
};
console.log(obj.getValue()); // 10
2. Method Invocation Pattern
A function stored as a property of an object and invoked through that object is a method. In this pattern, this is bound to the object that owns the method.
const person = {
name: 'Alice',
greet: function() {
return 'Hello, ' + this.name;
}
};
person.greet(); // 'Hello, Alice'
this is bound to person at the moment of invocation because greet is called through the person object.
3. Constructor Invocation Pattern
When a function is invoked with the new keyword, it acts as a constructor. A new object is created, this is bound to that new object, and the object is returned automatically unless the function explicitly returns a different object.
function Person(name) {
this.name = name;
}
Person.prototype.greet = function() {
return 'Hello, ' + this.name;
};
const alice = new Person('Alice');
console.log(alice.greet()); // 'Hello, Alice'
By convention, constructor functions are written with a capitalised first letter to signal that they should be called with new. Calling a constructor without new will bind this to the global object, which produces incorrect and potentially dangerous behaviour.
4. Apply Invocation Pattern
The apply pattern allows a function to be invoked with an explicitly supplied this value. Every function inherits call and apply from Function.prototype, which make this possible.
apply accepts the this value as the first argument and an array of arguments as the second.
function greet(greeting) {
return greeting + ', ' + this.name;
}
const user = { name: 'Bob' };
console.log(greet.apply(user, ['Hello'])); // 'Hello, Bob'
call works the same way but accepts arguments individually rather than as an array.
console.log(greet.call(user, 'Hello')); // 'Hello, Bob'
bind is a related method that creates a new function with this permanently bound to the provided value without invoking the function immediately.
const greetBob = greet.bind(user);
console.log(greetBob('Hi')); // 'Hi, Bob'
Why This Matters
The four patterns are the complete set of ways this is determined in JavaScript. Every time a function runs, one of these four patterns is active. Identifying which pattern applies in any given situation tells you exactly what this refers to without needing to guess.
// Which pattern is this?
document.querySelector('button').addEventListener('click', person.greet);
Here, greet is passed as a plain function reference and called by the event system, not through person. The method invocation pattern does not apply. this inside greet will not be person. This is a common source of bugs, and the fix is bind:
document.querySelector('button').addEventListener('click', person.greet.bind(person));
Knowing the four patterns makes bugs like this immediately visible.