Method Borrowing với call(), apply(), bind()
JavaScript cho phép **mượn phương thức** từ object này dùng cho object khác nhờ 3 phương thức của Function.prototype:
call(thisArg, arg1, arg2, ...)- Gọi ngay, truyền args riêng lẻ.apply(thisArg, [argsArray])- Gọi ngay, truyền args qua mảng.bind(thisArg, arg1, arg2, ...)- **Trả về hàm mới** với this đã gắn cố định.
Ví dụ minh họa
<!DOCTYPE html>
<html lang="vi">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Method Borrowing</title>
<style>
body { font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif; }
.result, .sample { font-size: 18px; font-weight: 500; color: rebeccapurple; font-family: monospace; }
.result { color: red; }
button { padding: 8px 16px; cursor: pointer; }
</style>
</head>
<body>
<h1>Mượn phương thức (Method Borrowing) trong JavaScript</h1>
<div class="sample"></div>
<div class="result"></div>
<button class="btn-demo">MƯỢN PHƯƠNG THỨC</button>
<h3>Nhấn nút để mượn phương thức welcome() từ obj cho obj1</h3>
<script>
const sampleEle = document.querySelector(".sample");
const resultEle = document.querySelector(".result");
const btn = document.querySelector(".btn-demo");
const obj = {
firstName: "Rohan",
lastName: "Sharma",
welcome() {
return "Chào mừng " + this.firstName + " " + this.lastName;
},
};
const obj1 = {
firstName: "John",
lastName: "Shaw",
};
sampleEle.textContent = "obj.welcome() = " + obj.welcome();
btn.addEventListener("click", () => {
// Mượn phương thức với call() - this = obj1
resultEle.textContent = "obj.welcome.call(obj1) = " + obj.welcome.call(obj1);
// Demo thêm apply() và bind()
resultEle.textContent += "\nobj.welcome.apply(obj1) = " + obj.welcome.apply(obj1);
const boundWelcome = obj.welcome.bind(obj1);
resultEle.textContent += "\nobj.welcome.bind(obj1)() = " + boundWelcome();
});
</script>
</body>
</html>
So sánh chi tiết
| Phương thức | Gọi ngay? | Truyền tham số | Trả về | Dùng khi |
|---|---|---|---|---|
call() | Có | Danh sách: fn.call(obj, 1, 2) | Kết quả hàm | Gọi một lần, args riêng lẻ |
apply() | Có | Mảng: fn.apply(obj, [1, 2]) | Kết quả hàm | Gọi một lần, args trong mảng |
bind() | Không | Danh sách: fn.bind(obj, 1, 2) | Hàm mới | Dùng lại nhiều lần, callback, event handler |
Ứng dụng thực tế
1. Mượn method của Array cho Array-like object
const arrayLike = { 0: 'a', 1: 'b', length: 2 };
// Mượn slice để chuyển thành mảng thật
const arr = Array.prototype.slice.call(arrayLike);
// Hoặc ES6:
const arr2 = [...arrayLike];
// Hoặc Array.from()
const arr3 = Array.from(arrayLike);
2. Giữ this cho callback/event handler
class Counter {
constructor() {
this.count = 0;
}
increment() {
this.count++;
}
}
const counter = new Counter();
const btn = document.querySelector('button');
// SAI: this = button
// btn.addEventListener('click', counter.increment);
// ĐÚNG: bind()
btn.addEventListener('click', counter.increment.bind(counter));
// HOẶC arrow function (ES6)
btn.addEventListener('click', () => counter.increment());
3. Partial Application với bind()
function multiply(a, b, c) {
return a * b * c;
}
const double = multiply.bind(null, 2); // a = 2
const triple = multiply.bind(null, 3); // a = 3
const doubleThenTriple = multiply.bind(null, 2, 3); // a=2, b=3
console.log(double(5)); // 2 * 5 * 1 = 10 (c còn thiếu)
console.log(double(5, 2)); // 2 * 5 * 2 = 20
console.log(doubleThenTriple(4)); // 2 * 3 * 4 = 24