Trong JavaScript, this đóng vai trò quan trọng trong việc xác định ngữ cảnh thực thi của một hàm. Tuy nhiên, this có thể thay đổi tùy vào cách gọi hàm, điều này đôi khi gây nhầm lẫn. Để kiểm soát tốt hơn this, JavaScript cung cấp ba phương thức hữu ích: call(), apply(), và bind().
Trước khi đi vào chi tiết từng phương thức, chúng ta cần hiểu rõ this trong JavaScript. this là một từ khóa đặc biệt đại diện cho đối tượng mà hàm đang được gọi trên đó. Giá trị của this thay đổi tùy theo cách hàm được gọi:
Khi gọi trong phạm vi toàn cục (global), this tham chiếu đến window (trong trình duyệt) hoặc global (trong Node.js). Khi gọi trong một object method, this tham chiếu đến object đó. Khi gọi trong một function thông thường, this có thể là undefined trong chế độ nghiêm ngặt (strict mode) hoặc tham chiếu đến window. Khi gọi trong một arrow function, this kế thừa từ phạm vi bao ngoài.
Có thể khá khó hiểu nếu bạn chưa nắm rõ bản chất của this. Đừng lo, ba phương thức call(), apply(), bind() sẽ giúp bạn kiểm soát this một cách đơn giản, tránh trường hợp this trỏ sai đối tượng.
1. call() - Gọi Hàm Trực Tiếp
call() là phương thức của đối tượng Function, cho phép gọi một hàm với một giá trị this được chỉ định và các đối số được truyền vào riêng lẻ.
Cú pháp
functionName.call(thisArg, arg1, arg2, ...)
Cơ chế hoạt động
Khi bạn gọi func.call(obj, arg1, arg2), JavaScript thực hiện các bước sau:
- Gán hàm func làm phương thức tạm thời của đối tượng obj
- Thực thi hàm với this trỏ đến obj
- Truyền các đối số arg1, arg2, ... vào hàm
- Xóa phương thức tạm thời và trả về kết quả
Use Case
Khi bạn muốn gọi một hàm từ một đối tượng khác mà không cần phải tạo một phương thức mới.
2. apply() - Giống call(), Nhưng Truyền Tham Số Dưới Dạng Mảng
apply() hoạt động tương tự như call(), nhưng thay vì truyền đối số riêng lẻ, nó nhận một mảng hoặc đối tượng giống mảng chứa các đối số.
Cú pháp
functionName.apply(thisArg, [argsArray])
Cơ chế hoạt động
Cơ chế hoạt động của apply() gần giống với call(), nhưng thay vì nhận các đối số riêng lẻ, nó nhận một mảng đối số:
- Gán hàm làm phương thức tạm thời của đối tượng thisArg
- Thực thi hàm với this trỏ đến thisArg
- Truyền các phần tử của mảng argsArray làm đối số
- Xóa phương thức tạm thời và trả về kết quả
Use cases
Thường được dùng trong các trường hợp thao tác với mảng:
- Tìm giá trị lớn nhất/nhỏ nhất trong mảng
- Mượn phương thức với đối số dạng mảng
3. bind() - Gán this, Nhưng Không Gọi Ngay
bind() tạo ra một hàm mới với ngữ cảnh this được cố định, không thực thi hàm ngay lập tức như call() và apply().
Cú pháp
const boundFunction = originalFunction.bind(thisArg[, arg1[, arg2[, ...]]])
Cơ chế hoạt động
Khi bạn gọi func.bind(obj, arg1, arg2), JavaScript:
- Tạo một hàm mới với this được gắn cố định với obj
- Các đối số arg1, arg2, ... được "ghi nhớ" và sẽ được truyền vào khi hàm mới được gọi
- Trả về hàm mới mà không thực thi nó
Use cases
- Giữ ngữ cảnh trong các hàm callback
- Gán trước this trong sự kiện DOM
const button = document.getElementById('myButton');
const user = { name: 'Charlie', showName: function() { console.log(this.name); }
};
button.addEventListener('click', user.showName.bind(user));
4. Khi Nào Dùng Cái Nào?
- Dùng call() khi cần gọi hàm ngay và có danh sách tham số riêng lẻ.
- Dùng apply() khi có dữ liệu dưới dạng mảng và cần gọi hàm ngay.
- Dùng bind() khi muốn tạo một bản sao của hàm để gọi sau, với this cố định.
Việc hiểu rõ về call(), apply(), và bind() là rất quan trọng trong JavaScript, đặc biệt khi làm việc với các hàm và đối tượng. Chúng giúp bạn quản lý ngữ cảnh của hàm một cách hiệu quả, từ đó làm cho mã nguồn dễ đọc và bảo trì hơn.