Skip to content
Plurafy

This Binding and Context

Understanding how 'this' keyword works in different contexts

Why do the following function calls produce different results?

const obj = {
name: 'Alice',
greet: function() {
console.log('Hello, ' + this.name);
}
};
obj.greet(); // ?
const greetFunc = obj.greet;
greetFunc(); // ?

The value of this is determined by how a function is called, not where it’s defined. This is called dynamic binding. Understanding different calling contexts is crucial for avoiding bugs.

const obj = {
name: 'Alice',
greet: function() {
console.log('Hello, ' + this.name);
}
};
obj.greet(); // "Hello, Alice" - called as method on obj
const greetFunc = obj.greet;
greetFunc(); // "Hello, undefined" - called as standalone function, this is window/undefined
function sayName() {
console.log(this.name);
}
const name = 'Global';
sayName(); // "Global" (in non-strict mode)
// In strict mode:
'use strict';
sayName(); // TypeError: Cannot read property 'name' of undefined
const user = {
name: 'Bob',
getName: function() {
console.log(this.name);
}
};
user.getName(); // "Bob" - this refers to user
function introduce(greeting) {
console.log(`${greeting}, my name is ${this.name}`);
}
const person = { name: 'Charlie' };
introduce.call(person, 'Hi'); // call: invoke immediately
introduce.apply(person, ['Hello']); // apply: pass array of args
const boundIntro = introduce.bind(person); // bind: return bound function
boundIntro('Hey');
function Person(name) {
this.name = name; // this refers to newly created object
}
const person = new Person('David');
console.log(person.name); // "David"
const obj = {
name: 'Eve',
regularFunc: function() {
console.log('Regular:', this.name); // "Eve"
const arrow = () => {
console.log('Arrow:', this.name); // "Eve" - inherits from enclosing scope
};
arrow();
},
arrowFunc: () => {
console.log('Method arrow:', this.name); // undefined - no enclosing object
}
};
obj.regularFunc(); // "Eve", "Eve"
obj.arrowFunc(); // undefined
// Problem: Lost context in callbacks
class Button {
constructor(label) {
this.label = label;
}
render() {
// document.addEventListener('click', this.onClick); // Wrong! this lost
}
onClick() {
console.log('Clicked:', this.label);
}
}
// Solution 1: Bind in constructor
class Button1 {
constructor(label) {
this.label = label;
this.onClick = this.onClick.bind(this);
}
onClick() {
console.log('Clicked:', this.label);
}
}
// Solution 2: Arrow function
class Button2 {
constructor(label) {
this.label = label;
}
onClick = () => {
console.log('Clicked:', this.label);
}
}
// Solution 3: Wrapper function
class Button3 {
constructor(label) {
this.label = label;
}
render() {
// document.addEventListener('click', () => this.onClick());
}
onClick() {
console.log('Clicked:', this.label);
}
}

Interview Tip: Using call/apply/bind Creatively

Section titled “Interview Tip: Using call/apply/bind Creatively”
// Get array methods on array-like objects
const arrayLike = { 0: 'a', 1: 'b', 2: 'c', length: 3 };
const result = Array.prototype.slice.call(arrayLike);
console.log(result); // ['a', 'b', 'c']
// Or use bind for partial application
function multiply(a, b, c) {
return a * b * c;
}
const double = multiply.bind(null, 2);
console.log(double(3, 4)); // 24