This Binding and Context
Understanding how 'this' keyword works in different contexts
Challenge
Section titled “Challenge”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(); // ?Explanation
Section titled “Explanation”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.
Solution
Section titled “Solution”const obj = { name: 'Alice', greet: function() { console.log('Hello, ' + this.name); }};
obj.greet(); // "Hello, Alice" - called as method on objconst greetFunc = obj.greet;greetFunc(); // "Hello, undefined" - called as standalone function, this is window/undefinedFour Binding Rules
Section titled “Four Binding Rules”1. Default Binding
Section titled “1. Default Binding”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 undefined2. Implicit Binding
Section titled “2. Implicit Binding”const user = { name: 'Bob', getName: function() { console.log(this.name); }};
user.getName(); // "Bob" - this refers to user3. Explicit Binding
Section titled “3. Explicit Binding”function introduce(greeting) { console.log(`${greeting}, my name is ${this.name}`);}
const person = { name: 'Charlie' };
introduce.call(person, 'Hi'); // call: invoke immediatelyintroduce.apply(person, ['Hello']); // apply: pass array of argsconst boundIntro = introduce.bind(person); // bind: return bound functionboundIntro('Hey');4. Constructor Binding
Section titled “4. Constructor Binding”function Person(name) { this.name = name; // this refers to newly created object}
const person = new Person('David');console.log(person.name); // "David"Arrow Functions and This
Section titled “Arrow Functions and This”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(); // undefinedSolving This Binding Issues
Section titled “Solving This Binding Issues”// Problem: Lost context in callbacksclass 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 constructorclass Button1 { constructor(label) { this.label = label; this.onClick = this.onClick.bind(this); } onClick() { console.log('Clicked:', this.label); }}
// Solution 2: Arrow functionclass Button2 { constructor(label) { this.label = label; } onClick = () => { console.log('Clicked:', this.label); }}
// Solution 3: Wrapper functionclass 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 objectsconst 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 applicationfunction multiply(a, b, c) { return a * b * c;}
const double = multiply.bind(null, 2);console.log(double(3, 4)); // 24