Phương thức apply() trong hàm trong JavaScript

Javascript nâng cao | by Học Javascript

Trong JavaScript, phương thức apply() là một công cụ quan trọng giúp lập trình viên kiểm soát giá trị this khi gọi một hàm. Nó cho phép chúng ta gọi một hàm với một đối tượng cụ thể và truyền các tham số dưới dạng một mảng.

So với call(), phương thức apply() đặc biệt hữu ích khi danh sách tham số có sẵn dưới dạng một mảng, giúp mã nguồn gọn gàng và linh hoạt hơn. Bên cạnh đó, apply() cũng thường được sử dụng để mượn phương thức từ object khác và làm việc với các hàm có tham số biến đổi như Math.max() hoặc Math.min().

Việc hiểu rõ cách hoạt động của apply() không chỉ giúp chúng ta tránh lỗi khi thao tác với this mà còn tối ưu hóa hiệu suất và tổ chức mã một cách hiệu quả hơn. Trong bài viết này, chúng ta sẽ tìm hiểu chi tiết về cú pháp, cách sử dụng, sự khác biệt giữa apply(), call()bind(), cũng như các ứng dụng thực tế của phương thức này.

Phương thức apply() trong JavaScript

Trong JavaScript, phương thức apply() là một công cụ quan trọng được sử dụng để gọi một hàm với một giá trị this cụ thể và truyền đối số dưới dạng một mảng hoặc một đối tượng giống mảng (array-like object).

Phương thức này có chức năng tương tự như call(), nhưng thay vì truyền từng tham số riêng lẻ, apply() yêu cầu tất cả tham số của hàm được đặt trong một mảng. Điều này giúp xử lý các trường hợp danh sách tham số có độ dài không cố định một cách dễ dàng và gọn gàng hơn.

Vai trò của apply() trong việc kiểm soát this khi gọi hàm

Một trong những vấn đề quan trọng trong JavaScript là giá trị của this bên trong một hàm có thể thay đổi tùy theo cách hàm được gọi. Nếu không kiểm soát đúng, this có thể trỏ đến window (trong môi trường trình duyệt) hoặc undefined (trong chế độ strict mode).

Phương thức apply() giúp giải quyết vấn đề này bằng cách cho phép chúng ta chỉ định một giá trị cụ thể cho this khi gọi hàm. Điều này đặc biệt hữu ích trong các trường hợp như:

  • Mượn phương thức từ một object khác: Dùng apply() để tái sử dụng một phương thức của một object khác mà không cần sao chép hoặc viết lại mã.
  • Gọi hàm với danh sách tham số không cố định: Khi có dữ liệu được lưu trữ dưới dạng mảng, apply() giúp truyền nó vào một hàm mà không cần giải nén từng phần tử.
  • Làm việc với các hàm có tham số biến đổi: apply() thường được sử dụng với các phương thức như Math.max() hoặc Math.min() để tìm giá trị lớn nhất/nhỏ nhất trong một mảng.

Ví dụ:

const person = {
    fullName: function() {
        return this.firstName + " " + this.lastName;
    }
};

const person1 = { firstName: "John", lastName: "Doe" };
const person2 = { firstName: "Jane", lastName: "Smith" };

console.log(person.fullName.apply(person1)); // "John Doe"
console.log(person.fullName.apply(person2)); // "Jane Smith"

So sánh sơ bộ apply() với call() và bind()

Phương thức Cách truyền tham số Gọi hàm ngay lập tức? Trả về một hàm mới?
call() Truyền tham số riêng lẻ Không
apply() Truyền tham số dạng mảng Không
bind() Truyền tham số riêng lẻ Không

apply() vs call():

  • Cả hai đều gọi hàm ngay lập tức.
  • apply() truyền tham số dưới dạng một mảng, trong khi call() truyền từng tham số riêng lẻ.
  • apply() hữu ích khi chúng ta có sẵn dữ liệu trong một mảng và muốn truyền toàn bộ vào hàm.

apply() vs bind():

  • apply() gọi hàm ngay lập tức, trong khi bind() trả về một hàm mới có this được cố định, cho phép gọi sau.
  • bind() thường được dùng khi cần tạo một bản sao của hàm để sử dụng sau này.

Ví dụ về sự khác biệt giữa apply()call():

function greet(greeting, punctuation) {
    console.log(greeting + ", " + this.name + punctuation);
}

const person = { name: "Alice" };

greet.call(person, "Hello", "!"); // "Hello, Alice!"
greet.apply(person, ["Hi", "!!"]); // "Hi, Alice!!"

Nhìn chung, apply() rất hữu ích khi chúng ta làm việc với dữ liệu có sẵn trong một mảng, giúp mã nguồn gọn gàng và linh hoạt hơn.

apply() là gì?

Trong JavaScript, apply() là một phương thức có sẵn của mọi hàm, được sử dụng để gọi một hàm với một giá trị this cụ thể và truyền tham số dưới dạng một mảng hoặc một đối tượng giống mảng (array-like object).

Phương thức này đặc biệt hữu ích khi làm việc với các hàm có danh sách tham số không xác định hoặc khi chúng ta cần chuyển đổi dữ liệu từ một mảng thành các đối số riêng lẻ cho một hàm.

Tính năng chính của apply()

  • Gọi hàm ngay lập tức với một giá trị this do người dùng chỉ định.
  • Truyền đối số dưới dạng một mảng, thay vì từng giá trị riêng lẻ như call().
  • Có thể sử dụng với các hàm có sẵn như Math.max() hoặc Math.min() để làm việc với mảng số.
  • Thường dùng để mượn phương thức từ một object khác, giúp tái sử dụng mã mà không cần sao chép phương thức.

Ví dụ minh họa:

function introduce(language, country) {
    console.log(`My name is ${this.name}. I speak ${language} and I live in ${country}.`);
}

const person = { name: "Alice" };

// Gọi hàm với apply(), truyền đối số dưới dạng mảng
introduce.apply(person, ["English", "USA"]);
// Output: My name is Alice. I speak English and I live in USA.

Cú pháp của apply()

Cú pháp của apply() như sau:

functionName.apply(thisArg, [arg1, arg2, ...]);

Trong đó:

  • functionName: Tên của hàm cần gọi.
  • thisArg: Giá trị của this khi thực thi hàm. Có thể là một object cụ thể hoặc null, undefined nếu không muốn gán this.
  • [arg1, arg2, ...]: Danh sách các tham số được truyền vào hàm dưới dạng một mảng.

Ví dụ chi tiết về cách sử dụng apply()

Gọi hàm với apply() và thay đổi this

Trong trường hợp này, chúng ta có một hàm dùng để giới thiệu một người. Khi sử dụng apply(), ta có thể thay đổi giá trị của this và truyền đối số dưới dạng mảng.

function greet(greeting, punctuation) {
    console.log(greeting + ", " + this.name + punctuation);
}

const person = { name: "Alice" };

greet.apply(person, ["Hello", "!"]); 
// Output: Hello, Alice!

Sử dụng apply() để mượn phương thức từ object khác

Trong JavaScript, chúng ta có thể sử dụng apply() để mượn phương thức từ một object khác mà không cần sao chép mã.

Ví dụ: Mượn phương thức getFullName từ object person và áp dụng nó cho person2.

const person1 = {
    firstName: "John",
    lastName: "Doe",
    getFullName: function() {
        return this.firstName + " " + this.lastName;
    }
};

const person2 = {
    firstName: "Jane",
    lastName: "Smith"
};

// Mượn phương thức từ person1 để dùng cho person2
console.log(person1.getFullName.apply(person2)); 
// Output: Jane Smith

Sử dụng apply() với Math.max()Math.min()

Một ứng dụng phổ biến của apply() là dùng nó với các phương thức như Math.max()Math.min() để tìm giá trị lớn nhất hoặc nhỏ nhất trong một mảng.

const numbers = [3, 7, 2, 8, 10];

// Dùng apply() để truyền toàn bộ mảng vào Math.max()
console.log(Math.max.apply(null, numbers)); 
// Output: 10

// Dùng apply() với Math.min()
console.log(Math.min.apply(null, numbers)); 
// Output: 2

Lưu ý: Trong ES6+, thay vì dùng apply(), chúng ta có thể sử dụng spread operator (...) để truyền các giá trị từ một mảng:

console.log(Math.max(...numbers)); 
Như vậy, apply() là một công cụ mạnh mẽ giúp kiểm soát this và làm việc với danh sách tham số linh hoạt hơn trong JavaScript.

Ví dụ minh họa về apply() trong JavaScript

Gọi hàm với apply() và thay đổi giá trị this

Khi gọi một hàm trong JavaScript, this mặc định là undefined hoặc tham chiếu đến object gọi hàm. Tuy nhiên, apply() cho phép thay đổi giá trị của this khi gọi hàm.

Ví dụ:

function introduce(language, country) {
    console.log(`My name is ${this.name}. I speak ${language} and I live in ${country}.`);
}

const person = { name: "Alice" };

// Gọi hàm với apply(), truyền đối số dưới dạng mảng
introduce.apply(person, ["English", "USA"]);
// Output: My name is Alice. I speak English and I live in USA.

Trong ví dụ trên, apply() giúp gán this cho object person, thay vì giá trị mặc định.

Sử dụng apply() để mượn phương thức từ object khác

Một ứng dụng quan trọng của apply() là mượn phương thức từ một object khác mà không cần sao chép mã.

Ví dụ:

const person1 = {
    fullName: function() {
        return this.firstName + " " + this.lastName;
    }
};

const person2 = {
    firstName: "Jane",
    lastName: "Smith"
};

// Mượn phương thức fullName của person1 và áp dụng cho person2
console.log(person1.fullName.apply(person2)); 
// Output: Jane Smith

Trong trường hợp này, person2 không có phương thức fullName(), nhưng ta có thể sử dụng apply() để áp dụng phương thức của person1.

Sử dụng apply() để truyền tham số dưới dạng mảng

apply() đặc biệt hữu ích khi dữ liệu tham số được lưu trong một mảng, giúp ta không cần truyền từng giá trị riêng lẻ.

Ví dụ:

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

const numbers = [3, 5, 7];

// Truyền mảng vào hàm sum bằng apply()
console.log(sum.apply(null, numbers)); 
// Output: 15

Nếu dùng call(), ta phải truyền từng tham số riêng lẻ, nhưng với apply(), ta chỉ cần truyền một mảng chứa tất cả các tham số.

So sánh apply() với call() và bind() trong JavaScript

So sánh apply() với call()

Phương thức Cách truyền tham số Gọi hàm ngay lập tức?
call() Truyền tham số riêng lẻ (arg1, arg2, ...)
apply() Truyền tham số dưới dạng một mảng ([arg1, arg2, ...])

Ví dụ so sánh call()apply()

function greet(greeting, punctuation) {
    console.log(greeting + ", " + this.name + punctuation);
}

const person = { name: "Alice" };

// Dùng call()
greet.call(person, "Hello", "!");  
// Output: Hello, Alice!

// Dùng apply()
greet.apply(person, ["Hello", "!"]);  
// Output: Hello, Alice!

Điểm khác biệt chính:

  • call() nhận tham số tách biệt.
  • apply() nhận danh sách tham số dưới dạng một mảng.

So sánh apply() với bind()

Phương thức Cách truyền tham số Gọi hàm ngay lập tức?
apply() Truyền tham số dưới dạng mảng
bind() Truyền tham số riêng lẻ Không (Trả về hàm mới)

Ví dụ so sánh apply()bind()

function greet(greeting, punctuation) {
    console.log(greeting + ", " + this.name + punctuation);
}

const person = { name: "Alice" };

// Dùng apply()
greet.apply(person, ["Hello", "!"]);  
// Output: Hello, Alice!

// Dùng bind()
const boundGreet = greet.bind(person, "Hello", "!");
boundGreet();  
// Output: Hello, Alice!

Điểm khác biệt chính:

  • apply() gọi hàm ngay lập tức.
  • bind() trả về một hàm mới, có thể gọi sau.

Ứng dụng thực tế của apply() trong JavaScript

Mượn phương thức từ object khác (Array.prototype.slice.apply(arguments))

Trong JavaScript, đối tượng arguments trong một hàm không phải là một mảng thực sự. Tuy nhiên, ta có thể sử dụng apply() để mượn phương thức từ Array.prototype.

Ví dụ:

function convertArgumentsToArray() {
    return Array.prototype.slice.apply(arguments);
}

console.log(convertArgumentsToArray(1, 2, 3, 4));
// Output: [1, 2, 3, 4]

Lợi ích: Giúp chuyển arguments thành mảng mà không cần lặp thủ công.

Gọi hàm với danh sách tham số động (khi dữ liệu được lưu trong mảng)

Khi làm việc với dữ liệu động (dữ liệu từ API hoặc người dùng nhập vào), apply() giúp truyền danh sách tham số mà không cần xác định trước số lượng phần tử.

Ví dụ:

function logDetails(name, age, city) {
    console.log(`${name} is ${age} years old and lives in ${city}.`);
}

const userData = ["John", 30, "New York"];

// Truyền dữ liệu động từ mảng vào hàm
logDetails.apply(null, userData);
// Output: John is 30 years old and lives in New York.

Lợi ích: Giúp xử lý dữ liệu động dễ dàng mà không cần truy xuất từng phần tử.

Sử dụng với Math.max() và Math.min() để tìm giá trị lớn nhất/nhỏ nhất trong mảng

Trong JavaScript, Math.max()Math.min() không chấp nhận mảng trực tiếp. apply() giúp ta truyền một mảng số vào hai hàm này.

Ví dụ:

const numbers = [5, 2, 9, 1, 7];

console.log(Math.max.apply(null, numbers));  
// Output: 9

console.log(Math.min.apply(null, numbers));  
// Output: 1

Lợi ích:

  • Giúp tìm giá trị lớn nhất/nhỏ nhất trong mảng mà không cần lặp thủ công.
  • Thay thế vòng lặp for hoặc reduce() trong nhiều trường hợp.

Lưu ý: Trong ES6, ta có thể thay thế apply() bằng spread operator (...):

console.log(Math.max(...numbers)); // 9
console.log(Math.min(...numbers)); // 1

Kết bài

Phương thức apply() trong JavaScript là một công cụ mạnh mẽ giúp kiểm soát giá trị của this và truyền tham số dưới dạng mảng khi gọi hàm. So với call(), apply() đặc biệt hữu ích khi làm việc với danh sách tham số động. Ngoài ra, apply() cũng hỗ trợ các tình huống như mượn phương thức từ object khác, xử lý arguments, và tối ưu hóa việc tìm giá trị lớn nhất/nhỏ nhất trong một mảng.

Mặc dù trong ES6, toán tử spread (...) có thể thay thế apply() trong nhiều trường hợp, nhưng apply() vẫn là một lựa chọn quan trọng trong các phiên bản JavaScript cũ và các tình huống cần thao tác trực tiếp với this. Việc hiểu rõ sự khác biệt giữa call(), apply(), và bind() sẽ giúp lập trình viên viết mã linh hoạt, tối ưu và tránh lỗi trong quá trình phát triển ứng dụng.

Tóm lại, nắm vững cách sử dụng apply() không chỉ giúp bạn kiểm soát tốt hơn việc gọi hàm mà còn mở rộng khả năng tái sử dụng code một cách hiệu quả!

Bài viết liên quan