In JavaScript, method borrowing refers to the practice of using methods from one object in the context of another object.
This can be extremely useful when an object lacks certain methods, but you want to reuse an existing method from another object without redefining it.
You can borrow methods using JavaScript's built-in call(), apply(), and bind() functions. This tutorial will explore these concepts with practical examples.
1. Using call() for Method Borrowing
The call() method allows you to call a function with a specific this value and arguments provided individually. This can be used to borrow methods from one object and use them on another.
Syntax
functionName.call(thisArg, arg1, arg2, ...);
thisArg: The object to use as this inside the function.
arg1, arg2, …: Arguments to pass to the function.
Example 1: Borrowing a Method with call()
const person1 = { name: 'Alice', greet: function(greeting) { console.log(`${greeting}, my name is ${this.name}.`); } }; const person2 = { name: 'Bob' }; // Borrowing the `greet` method from `person1` to use on `person2` person1.greet.call(person2, 'Hello'); // Output: "Hello, my name is Bob."
Explanation: Here, the greet method belongs to person1. By using call(), we borrow greet and call it with person2 as this, making it act as if person2 had the method.
2. Using apply() for Method Borrowing
The apply() method is similar to call(), but it accepts the arguments as an array. This is useful when you want to pass an array of values to a borrowed method.
Syntax
functionName.apply(thisArg, [arg1, arg2, ...]);
thisArg: The object to use as this inside the function.
[arg1, arg2, …]: An array of arguments to pass to the function.
Example 2: Borrowing a Method with apply()
const person1 = { name: 'Alice', introduce: function(age, city) { console.log(`Hello, I am ${this.name}, I am ${age} years old and I live in ${city}.`); } }; const person2 = { name: 'Bob' }; // Borrowing the `introduce` method from `person1` to use on `person2` person1.introduce.apply(person2, [25, 'New York']); // Output: "Hello, I am Bob, I am 25 years old and I live in New York."
Explanation: The introduce method is borrowed from person1 and used with person2 as this, with arguments passed as an array.
3. Using bind() for Method Borrowing
The bind() method creates a new function that, when called, has its this keyword set to the provided value. Unlike call() and apply(), bind() does not immediately invoke the function. Instead, it returns a new function with the specified this value and arguments.
Syntax
const boundFunction = functionName.bind(thisArg, arg1, arg2, ...);
thisArg: The object to use as this inside the function.
arg1, arg2, …: Arguments to pass to the function when it is called.
Example 3: Borrowing a Method with bind()
const person1 = { name: 'Alice', describe: function(job) { console.log(`My name is ${this.name} and I am a ${job}.`); } }; const person2 = { name: 'Bob' }; // Borrowing the `describe` method using `bind` const describeBob = person1.describe.bind(person2, 'developer'); describeBob(); // Output: "My name is Bob and I am a developer."
Explanation: The bind() method returns a new function (describeBob) with this set to person2. It can be called later with any arguments specified during the binding.
4. Using Built-in Methods with Method Borrowing
You can borrow built-in array methods to operate on other objects, like strings or arguments objects. This is particularly useful when working with objects that are array-like but lack the methods of an array.
Example 4: Borrowing Array Methods
const string = 'Hello, world!'; const stringToArray = Array.prototype.slice.call(string); console.log(stringToArray); // Output: ['H', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!']
Explanation: Here, Array.prototype.slice is borrowed and used on a string to convert it into an array of characters.
Example 5: Using apply() with Math Functions
const numbers = [4, 2, 8, 6]; // Using Math.max with `apply` to find the maximum in an array const maxNumber = Math.max.apply(null, numbers); console.log(maxNumber); // Output: 8
Explanation: The Math.max method is borrowed using apply() to find the maximum value in an array. apply() allows passing the array elements as individual arguments to Math.max.
5. Method Borrowing and Arrow Functions
Arrow functions do not have their own this binding; they inherit this from the surrounding scope. Because of this behavior, you cannot use call(), apply(), or bind() to change the this value of an arrow function.
Example 6: Arrow Functions and Borrowing
const person = { name: 'Alice', greet: () => { console.log(`Hello, my name is ${this.name}`); } }; const anotherPerson = { name: 'Bob' }; person.greet.call(anotherPerson); // Output: "Hello, my name is undefined"
Explanation: The greet method is an arrow function and does not have its own this context. As a result, this.name is undefined, and call() cannot change the this value.
6. Practical Use Case: Borrowing Array Methods for arguments Object
The arguments object in functions is array-like but does not have array methods like forEach, map, etc. You can borrow array methods to work with arguments.
Example 7: Borrowing slice() for arguments
function listArguments() { const argsArray = Array.prototype.slice.call(arguments); argsArray.forEach(arg => console.log(arg)); } listArguments('apple', 'banana', 'cherry'); // Output: // apple // banana // cherry
Explanation: The arguments object is converted to an array using Array.prototype.slice, allowing us to use forEach to log each argument.
Conclusion
Method borrowing in JavaScript allows you to reuse functions and methods from other objects, promoting code reuse and reducing redundancy.
This technique primarily leverages call(), apply(), and bind() to change the context (this) under which a function operates. Understanding how to use these methods effectively enables you to write more flexible and dynamic code.
Here’s a quick recap:
call(): Invokes a function with a specified this context and arguments passed individually.
apply(): Similar to call(), but accepts arguments as an array.
bind(): Returns a new function with a specified this context and predefined arguments, which can be called later.
Experiment with these methods to master method borrowing and make your JavaScript code more modular and reusable!