Trong JavaScript, các phương thức call(), apply(), và bind() đều được sử dụng để thiết lập giá trị của từ khóa this trong một hàm, cho phép bạn kiểm soát ngữ cảnh khi hàm được gọi. Mặc dù chúng có mục đích tương tự nhau, nhưng cách sử dụng và hành vi của chúng có những khác biệt nhỏ.

1. call()

Phương thức call() gọi một hàm với một giá trị this cụ thể, và bạn có thể truyền các đối số vào hàm đó lần lượt.

Cú pháp:

func.call(thisArg, arg1, arg2, ...);
  • thisArg: Giá trị mà this sẽ được gán khi hàm được thực thi.
  • arg1, arg2, ...: Các đối số được truyền vào hàm.

Ví dụ:

function greet(greeting, name) {
    console.log(greeting + ', ' + name + '!');
}

greet.call(null, 'Hello', 'Alice');  // 'Hello, Alice!'

Ở đây, phương thức call() được sử dụng để gọi hàm greet() với giá trị thisnull và truyền hai đối số là 'Hello''Alice'.

2. apply()

Phương thức apply() tương tự như call(), nhưng thay vì truyền các đối số riêng lẻ, bạn truyền chúng dưới dạng một mảng.

Cú pháp:

func.apply(thisArg, [argsArray]);
  • thisArg: Giá trị mà this sẽ được gán khi hàm được thực thi.
  • argsArray: Một mảng chứa các đối số cần truyền vào hàm.

Ví dụ:

function greet(greeting, name) {
    console.log(greeting + ', ' + name + '!');
}

greet.apply(null, ['Hi', 'Bob']);  // 'Hi, Bob!'

Ở đây, apply() truyền các đối số dưới dạng một mảng ['Hi', 'Bob'].

Sự khác biệt giữa call()apply()

  • call(): Các đối số được truyền lần lượt.
  • apply(): Các đối số được truyền dưới dạng mảng.

Ví dụ so sánh:

function sum(a, b) {
    return a + b;
}

console.log(sum.call(null, 2, 3));  // 5
console.log(sum.apply(null, [2, 3]));  // 5

3. bind()

Phương thức bind() không thực thi hàm ngay lập tức mà trả về một bản sao của hàm với giá trị this được cố định (bound) vào giá trị cụ thể và các đối số có thể được truyền sẵn nếu muốn. Hàm trả về có thể được gọi sau này.

Cú pháp:

let boundFunc = func.bind(thisArg, arg1, arg2, ...);
  • thisArg: Giá trị mà this sẽ được gán khi hàm được thực thi.
  • arg1, arg2, ...: Các đối số sẽ được cố định cho hàm.

Ví dụ:

let person = {
    name: 'Charlie',
    greet: function() {
        console.log('Hello, ' + this.name);
    }
};

let greetCharlie = person.greet.bind(person);
greetCharlie();  // 'Hello, Charlie'

Trong ví dụ này, bind() tạo một hàm mới greetCharlie với this được gán cố định vào đối tượng person. Hàm này có thể được gọi sau đó, và giá trị của this luôn là person.

Ví dụ với đối số:

function multiply(a, b) {
    return a * b;
}

let double = multiply.bind(null, 2);
console.log(double(5));  // 10

Ở đây, bind() tạo một hàm double mà giá trị a luôn là 2, chỉ cần truyền b khi gọi hàm sau đó.

Sự khác biệt giữa call(), apply(), và bind()

Phương thứcThực thi ngay lập tức?Truyền đối sốMục đích
call()Các đối số được truyền lần lượtGọi hàm với this và đối số cụ thể.
apply()Đối số truyền dưới dạng mảngGọi hàm với this và đối số cụ thể trong mảng.
bind()KhôngĐối số được cố định trướcTrả về một hàm mới với this và đối số cố định.

Khi nào nên sử dụng?

  • call()apply(): Khi bạn cần thực thi một hàm ngay lập tức với một ngữ cảnh this cụ thể. Dùng call() nếu bạn có các đối số riêng lẻ, dùng apply() nếu bạn có các đối số trong một mảng.
  • bind(): Khi bạn cần tạo một phiên bản của hàm có this và/hoặc đối số cố định, đặc biệt hữu ích khi truyền hàm như là callback mà bạn muốn giữ ngữ cảnh của this.

Những phương thức này là công cụ mạnh mẽ giúp kiểm soát cách hàm hoạt động với this và các đối số, giúp bạn dễ dàng làm việc với các hàm trong các trường hợp đặc biệt hoặc phức tạp.