Call, Apply, Bind
These methods allow you to explicitly set the this
context when calling functions.
call() Method
javascript// call(thisArg, arg1, arg2, ...) function greet(greeting, punctuation) { return `${greeting}, ${this.name}${punctuation}`; } const person = { name: "Alice" }; // Use call to set 'this' to person console.log(greet.call(person, "Hello", "!")); // "Hello, Alice!" console.log(greet.call(person, "Hi", ".")); // "Hi, Alice." // Without call console.log(greet("Hello", "!")); // "Hello, undefined!"
apply() Method
javascript// apply(thisArg, [argsArray]) function introduce(greeting, age, city) { return `${greeting}, I'm ${this.name}, ${age} years old from ${city}`; } const user = { name: "Bob" }; const args = ["Hi", 25, "New York"]; // Use apply with array of arguments console.log(introduce.apply(user, args)); // "Hi, I'm Bob, 25 years old from New York" // call vs apply console.log(introduce.call(user, "Hi", 25, "New York")); // Same result console.log(introduce.apply(user, ["Hi", 25, "New York"])); // Same result
bind() Method
javascript// bind(thisArg, arg1, arg2, ...) - returns new function function sayHello() { return `Hello, ${this.name}!`; } const person1 = { name: "Charlie" }; const person2 = { name: "Diana" }; // Create bound functions const sayHelloToCharlie = sayHello.bind(person1); const sayHelloToDiana = sayHello.bind(person2); console.log(sayHelloToCharlie()); // "Hello, Charlie!" console.log(sayHelloToDiana()); // "Hello, Diana!" // Original function unchanged console.log(sayHello()); // "Hello, undefined!"
Practical Examples
1. Borrowing Methods
javascriptconst person = { firstName: "John", lastName: "Doe", getFullName: function() { return `${this.firstName} ${this.lastName}`; } }; const anotherPerson = { firstName: "Jane", lastName: "Smith" }; // Borrow method from person console.log(person.getFullName.call(anotherPerson)); // "Jane Smith" // Array-like objects borrowing array methods const arrayLike = { 0: "a", 1: "b", 2: "c", length: 3 }; const realArray = Array.prototype.slice.call(arrayLike); console.log(realArray); // ["a", "b", "c"]
2. Finding Max/Min in Arrays
javascriptconst numbers = [5, 2, 8, 1, 9]; // Math.max/min don't accept arrays, but we can use apply const max = Math.max.apply(null, numbers); const min = Math.min.apply(null, numbers); console.log(max); // 9 console.log(min); // 1 // Modern alternative with spread operator console.log(Math.max(...numbers)); // 9 console.log(Math.min(...numbers)); // 1
3. Event Handlers with Bind
javascriptclass Button { constructor(element) { this.element = element; this.clickCount = 0; // Bind 'this' to maintain context this.element.addEventListener('click', this.handleClick.bind(this)); } handleClick() { this.clickCount++; console.log(`Button clicked ${this.clickCount} times`); } } // Usage const button = document.querySelector('#myButton'); const buttonHandler = new Button(button);
4. Partial Application with Bind
javascriptfunction multiply(a, b, c) { return a * b * c; } // Create partially applied functions const multiplyByTwo = multiply.bind(null, 2); const multiplyByTwoAndThree = multiply.bind(null, 2, 3); console.log(multiplyByTwo(5, 3)); // 2 * 5 * 3 = 30 console.log(multiplyByTwoAndThree(4)); // 2 * 3 * 4 = 24 // Practical example: API calls function apiCall(method, url, data) { return fetch(url, { method: method, body: JSON.stringify(data), headers: { 'Content-Type': 'application/json' } }); } const postRequest = apiCall.bind(null, 'POST'); const putRequest = apiCall.bind(null, 'PUT'); // Now we can use them easily postRequest('/api/users', { name: 'John' }); putRequest('/api/users/1', { name: 'Jane' });
Advanced Patterns
1. Method Chaining with Bind
javascriptconst calculator = { value: 0, add: function(n) { this.value += n; return this; }, multiply: function(n) { this.value *= n; return this; }, getValue: function() { return this.value; } }; // Create bound methods const add5 = calculator.add.bind(calculator, 5); const multiply2 = calculator.multiply.bind(calculator, 2); // Use them add5(); multiply2(); console.log(calculator.getValue()); // 10
2. Function Composition
javascriptfunction compose(f, g) { return function(x) { return f.call(this, g.call(this, x)); }; } const addOne = x => x + 1; const double = x => x * 2; const addOneThenDouble = compose(double, addOne); console.log(addOneThenDouble(3)); // (3 + 1) * 2 = 8
3. Debouncing with Bind
javascriptfunction debounce(func, delay) { let timeoutId; return function(...args) { clearTimeout(timeoutId); timeoutId = setTimeout(() => { func.apply(this, args); }, delay); }; } const searchHandler = { query: '', search: function(term) { this.query = term; console.log(`Searching for: ${this.query}`); } }; // Create debounced version const debouncedSearch = debounce(searchHandler.search.bind(searchHandler), 300); // Usage in input handler const searchInput = document.querySelector('#search'); searchInput?.addEventListener('input', (e) => { debouncedSearch(e.target.value); });
Common Use Cases
javascript// 1. Converting NodeList to Array const nodeList = document.querySelectorAll('div'); const divArray = Array.prototype.slice.call(nodeList); // 2. Checking if value is array function isArray(value) { return Object.prototype.toString.call(value) === '[object Array]'; } // 3. Creating utility functions const log = console.log.bind(console); const warn = console.warn.bind(console); const error = console.error.bind(console); log('This is a log message'); warn('This is a warning'); error('This is an error'); // 4. Creating specialized functions function greetPerson(greeting, name) { return `${greeting}, ${name}!`; } const sayHello = greetPerson.bind(null, 'Hello'); const sayGoodbye = greetPerson.bind(null, 'Goodbye'); console.log(sayHello('Alice')); // "Hello, Alice!" console.log(sayGoodbye('Bob')); // "Goodbye, Bob!"
Performance Considerations
javascript// bind() creates new function each time class Component { constructor() { this.handleClick = this.handleClick.bind(this); // Do this once } handleClick() { console.log('Clicked'); } render() { // Don't do this - creates new function each render // return <button onClick={this.handleClick.bind(this)}>Click</button>; // Do this instead return `<button onclick="handleClick()">Click</button>`; } } // Alternative: Arrow functions in class properties class ModernComponent { handleClick = () => { console.log('Clicked'); } render() { // 'this' is automatically bound return `<button onclick="handleClick()">Click</button>`; } }