Cách Dùng Hàm Bậc Cao Trong JavaScript
Các hàm nhận một hàm khác làm đối số hoặc xác định một hàm làm giá trị trả về, được gọi là các hàm bậc cao (higher-order function).
JavaScript có thể chấp nhận các hàm bậc cao. Khả năng xử lý các hàm bậc cao, cùng với các đặc điểm khác, khiến JavaScript trở thành một trong những ngôn ngữ lập trình rất phù hợp cho việc lập trình hàm.
JavaScript xem Function như công dân hạng nhất
Bạn có thể đã nghe nói rằng các hàm JavaScript là công dân hạng nhất. Điều này có nghĩa là các hàm trong JavaScript là các đối tượng.
Hàm bậc cao có kiểu Object
, chúng có thể được gán làm giá trị của một biến, và chúng có thể được truyền và trả về giống như bất kỳ biến tham chiếu nào khác.
Các hàm hạng nhất cung cấp cho JavaScript sức mạnh đặc biệt và cho phép chúng ta hưởng lợi từ các hàm bậc cao.
Bởi vì hàm là đối tượng, JavaScript là một trong những ngôn ngữ lập trình phổ biến trong việc đem đến cách tiếp cận tự nhiên cho lập trình hàm.
Trên thực tế, các hàm hạng nhất được tích hợp thật sâu vào JavaScript đến nỗi chắc hẳn bạn đã sử dụng chúng mà không hề nghĩ đến.
Các hàm bậc cao có thể lấy một hàm làm đối số
Nếu bạn đã lập trình nhiều với JavaScript, có thể bạn đã bắt gặp các hàm có sử dụng lệnh gọi lại (callback).
Hàm gọi lại là một hàm được thực thi khi kết thúc một thao tác, sau khi tất cả các thao tác khác hoàn tất.
Thông thường, chúng ta truyền hàm này như một đối số sau cùng, sau các tham số khác. Nó thường được xác định nội tuyến dưới dạng hàm ẩn danh. Các hàm gọi lại dựa vào khả năng của JavaScript để xử lý các hàm bậc cao.
JavaScript là một ngôn ngữ đơn luồng. Điều này có nghĩa là chỉ có một thao tác có thể thực hiện tại một thời điểm.
Để tránh các thao tác chặn lẫn nhau hoặc luồng chính của hệ thống (từ đó có thể gây ra deadlock), engine sẽ đảm bảo tất cả các thao tác thực thi theo thứ tự. Chúng được xếp hàng dọc theo chuỗi đơn này, cho đến khi và tuần tự đến lượt một cách an toàn.
Khả năng truyền vào một hàm dưới dạng đối số và chạy nó sau khi các thao tác khác của hàm mẹ hoàn tất là điều cần thiết đối để một ngôn ngữ có thể hỗ trợ các hàm bậc cao.
Các hàm gọi lại trong JavaScript cho phép thực hiện hành vi không đồng bộ, do đó, một tập lệnh có thể tiếp tục thực thi các hàm hoặc thao tác khác trong khi chờ kết quả.
Khả năng chuyển một hàm gọi lại là rất quan trọng khi xử lý các tài nguyên có thể trả về kết quả sau một khoảng thời gian không xác định.
Mô hình hàm bậc cao này rất hữu ích trong việc phát triển web. Một tập lệnh có thể gửi một yêu cầu đến máy chủ, và sau đó cần xử lý phản hồi bất cứ khi nào nó đến, mà không yêu cầu bất kỳ thông tin nào về độ trễ mạng hoặc thời gian xử lý của máy chủ.
Node.js thường xuyên sử dụng các hàm gọi lại để sử dụng hiệu quả tài nguyên máy chủ. Cách tiếp cận không đồng bộ này cũng hữu ích trong trường hợp ứng dụng chờ người dùng nhập thông tin trước khi thực hiện một hàm.
Ví dụ: Chuyển một hàm cảnh báo đến Element Event Listener
Hãy xem xét đoạn mã JavaScript đơn giản này để thêmevent listener vào một nút.
So Clickable
document.getElementById("clicker").addEventListener("click", function() {
alert("you triggered " + this.id);
});
Tập lệnh này sử dụng một hàm nội tuyến ẩn danh để hiển thị cảnh báo.
Ta cũng có thể dễ dàng sử dụng một hàm được xác định riêng, và chuyển hàm được đặt tên đó vào phương thức addEventListener
:
var proveIt = function() {
alert("you triggered " + this.id);
};
document.getElementById("clicker").addEventListener("click", proveIt);
Như vậy, vừa rồi chúng ta không chỉ tận dụng sức mạnh của hàm bậc cao, mà còn làm cho mã của mình dễ đọc và linh hoạt hơn, đồng thời tách biệt hàm cho các tác vụ khác nhau (nhận diện lượt nhấp chuột vs. cảnh báo người dùng).
Cách hành bậc cao hỗ trợ việc tái sử dụng mã
Hàm proveIt()
nhỏ của chúng ta độc lập về cấu trúc với mã xung quanh nó, luôn trả về id
của bất kỳ phần tử nào được kích hoạt. Cách tiếp cận thiết kế hàm này là cốt lõi của lập trình hàm.
Đoạn mã này có thể tồn tại trong bất kỳ ngữ cảnh nào khi bạn hiển thị cảnh báo với id
của một phần tử, và có thể được gọi với bất kỳ event listener nào.
Khả năng thay thế một hàm nội tuyến bằng một hàm được xác định và đặt tên riêng sẽ mở ra rất nhiều khả năng.
Trong lập trình hàm, chúng ta nên cố gắng phát triển các hàm thuần túy không làm thay đổi dữ liệu bên ngoài và luôn trả về cùng một kết quả cho cùng một đầu vào.
Giờ đây, chúng ta đã có một trong những công cụ thiết yếu để phát triển một thư viện gồm các hàm nhỏ, bậc cao và chuyên biệt hơn mà bạn có thể sử dụng trong bất kỳ ứng dụng nào.
Lưu ý: Passing Functions vs. Passing Function Object
Lưu ý rằng chúng ta đã chuyển qua proveIt
chứ không phải là proveIt()
vào hàm addEventListener
.
Khi bạn truyền một hàm theo tên mà không có dấu ngoặc đơn, bạn đang truyền chính đối tượng hàm.
Khi bạn chuyển nó bằng dấu ngoặc đơn, bạn đang kết quả của việc thực thi hàm đó.
Trả về hàm dưới dạng kết quả với các hàm bậc cao
Ngoài việc lấy các hàm làm đối số, JavaScript cho phép các hàm trả về kết quả là các hàm khác.
Như vậy là hiển nhiên, vì hàm đơn giản cũng chỉ là các đối tượng mà thôi. Các đối tượng (bao gồm cả hàm) có thể được xác định làm giá trị trả về của một hàm, giống như chuỗi, mảng hoặc các giá trị khác.
Nhưng trả về kết quả dưới dạng hàm có nghĩa là gì?
Hàm là một cách hiệu quả để phân tách vấn đề và tạo ra các đoạn mã có thể tái sử dụng. Khi chúng ta xác định một hàm làm giá trị trả về của một hàm bậc cao hơn, nó có thể dùng làm khuôn mẫu cho các hàm mới!
Giả sử bạn đã đọc quá nhiều bài báo về Millennials và cảm thấy nhàm chán. Bạn quyết định thay thế từ Millennials bằng cụm từ Snake People.
Sự thúc đẩy của bạn có thể chỉ đơn giản là viết một hàm thực hiện việc thay thế đoạn text đó trên bất kỳ văn bản nào bạn đã chuyển cho nó:
var snakify = function(text) {
return text.replace(/millenials/ig, "Snake People");
};
console.log(snakify("The Millenials are always up to something."));
// The Snake People are always up to something.
Ta có thể làm theo cách trên, nhưng nó khá cụ thể cho riêng tình huống này. Có lẽ cũng sẽ không thích các bài báo về Baby Boomers. Bạn cũng muốn tạo một hàm tùy chỉnh cho chúng.
Nhưng ngay cả với một hàm đơn giản như vậy, bạn không muốn phải lặp lại mã mà bạn đã viết khi bạn có thể bắt đầu với một hàm bậc cao.
var hippify = function(te xt) {
return text.replace(/baby boomers/ig, "Aging Hippies");
};
console.log(hippify("The Baby Boomers just look the other way."));
// The Aging Hippies just look the other way.
Nhưng điều gì sẽ xảy ra nếu bạn quyết định rằng bạn muốn làm một điều gì đó kỳ công hơn để bảo toàn trường hợp trong chuỗi nguyên mẫu? Bạn sẽ phải sửa đổi cả hai hàm mới của mình để thực hiện điều này.
Thật là rắc rối, và như vậy mã của bạn trở nên rời rạc và khó đọc hơn. Trong những tình huống như thế này, chúng ta có thể sử dụng một hàm bậc cao làm một giải pháp.
Xây Dựng Template Hàm Bậc Cao
Điều bạn thực sự muốn là sự linh hoạt để có thể thay thế bất kỳ văn bản nào bằng một đoạn văn bản khác trong một hàm mẫu và xác định hành vi đó làm hàm cơ sở mà từ đó bạn có thể xây dựng các hàm tùy chỉnh mới.
Với khả năng gán các hàm dưới dạng giá trị trả về, JavaScript đưa ra các cách để làm cho kịch bản đó trở nên thuận tiện hơn nhiều:
var attitude = function(original, replacement, source) {
return function(source) {
return source.replace(original, replacement);
};
};
var snakify = attitude(/millenials/ig, "Snake People");
var hippify = attitude(/baby boomers/ig, "Aging Hippies");
console.log(snakify("The Millenials are always up to something."));
// The Snake People are always up to something.
console.log(hippify("The Baby Boomers just look the other way."));
// The Aging Hippies just look the other way.
Những gì chúng ta đã làm là tách đoạn mã thực sự có chức năng thành một hàm attitude
linh hoạt và có thể mở rộng. Nó đóng gói tất cả công việc cần thiết để sửa đổi bất kỳ chuỗi đầu vào nào bằng cách sử dụng cụm từ gốc làm giá trị ban đầu, và xuất ra một cụm từ thay thế với một attitude.
Chúng ta thu được gì khi xác định hàm mới này như một tham chiếu đến hàm attitude
bậc cao hơn, được điền trước bằng hai đối số đầu tiên mà nó tiếp nhận? Nó cho phép hàm mới lấy bất kỳ văn bản nào bạn chuyển cho nó, và sử dụng đối số đó trong hàm trả về mà chúng ta đã xác định là đầu ra của hàm attitude
.
Các hàm JavaScript không quan tâm đến số lượng đối số mà bạn pass cho chúng.
Nếu đối số thứ hai bị thiếu, nó sẽ coi đó là không xác định. Và sẽ hoạt động giống như khi chúng ta chọn không cung cấp đối số thứ ba, hoặc bất kỳ số lượng đối số bổ sung nào.
Hơn nữa, bạn có thể pass đối số bổ sung đó vào sau. Bạn có thể làm điều này khi bạn đã xác định hàm bậc cao mà bạn muốn gọi, như vừa được minh họa.
Chỉ cần xác định nó làm tham chiếu đến một hàm được hàm bậc cao trả về với một hoặc nhiều đối số không được xác định.
Hãy xem lại một vài lần nếu bạn cần, để hoàn toàn hiểu được chuyện gì đang xảy ra.
Chúng ta đang tạo một template hàm bậc cao trả về một hàm khác. Sau đó, chúng ta sẽ xác định hàm mới được trả về đó, trừ đi một thuộc tính, làm một tiền trình tùy chỉnh của hàm mẫu template.
Tất cả các hàm bạn tạo theo cách này sẽ kế thừa mã hiện hành từ hàm bậc cao hơn. Tuy nhiên, bạn có thể xác định trước chúng bằng các đối số mặc định khác nhau.
Bạn đã đang dùng hàm bậc cao rồi đấy
Các hàm bậc cao là một phần rất cơ bản đối với cách thức hoạt động của JavaScript đến nỗi bạn có thể đã dùng qua chúng.
Mỗi khi pass một hàm ẩn danh hoặc hàm call back, bạn thực ra đang lấy giá trị mà hàm được pass trả về và sử dụng giá trị đó làm đối số cho một hàm khác (chẳng hạn như với các hàm mũi tên).
Các nhà phát triển sớm làm quen với các hàm bậc cao trong quá trình học JavaScript. Nó vốn có trong thiết kế của JavaScript nên nhu cầu tìm hiểu về các khái niệm liên đới đến hàm mũi tên hoặc callback có thể không phát sinh cho đến sau này.
Khả năng gán các hàm trả về các hàm khác giúp mở rộng sự tiện lợi của JavaScript. Các hàm bậc cao cho phép chúng ta tạo ra các hàm có tên tùy chỉnh để thực hiện các tác vụ chuyên biệt với mã mẫu được chia sẻ từ một hàm bậc nhất.
Mỗi hàm này có thể kế thừa bất kỳ cải tiến nào được thực hiện trong hàm bậc cao. Điều này giúp chúng ta tránh trùng lặp mã và giữ cho mã nguồn sạch và dễ đọc.
Nếu bạn đảm bảo các hàm của mình là thuần túy (chúng không thay đổi các giá trị bên ngoài và luôn trả về cùng một giá trị cho bất kỳ đầu vào nhất định nào), bạn có thể tạo các bài kiểm tra để xác minh rằng các thay đổi mã của bạn không phá vỡ bất kỳ điều gì khi bạn cập nhật các hàm bậc nhất của mình .
Lời kết
Bây giờ bạn đã biết cách hoạt động của một hàm bậc cao, bạn có thể bắt đầu suy nghĩ về những cách để có thể tận dụng khái niệm này trong các dự án của riêng mình.
Một trong những điều tuyệt vời về JavaScript là bạn có thể kết hợp các kỹ thuật hàm ngay với mã mà bạn đã quen thuộc.Hãy thử một số thí nghiệm với hàm bậc cao, bạn sẽ sớm quen với tính linh hoạt bổ sung mà chúng cung cấp.
Theo Sitepoint
Nhận xét
Đăng nhận xét