Tính năng currying trong JavaScript có thể giúp bạn sắp xếp code gọn gàng và cho bạn cách đánh giá mới về hoạt động của các hàm. Kỹ thuật currying là lựa chọn lý tưởng khi bạn muốn chia logic phức tạp thành các đoạn code nhỏ hơn, dễ quản lý và khép kín.
Dưới đây là những điều bạn cần biết về cách dùng kỹ thuật currying trong JavaScript.
Currying là gì?
Currying được đặt tên theo nhà toán học Haskell B. Curry, và khái niệm bắt nguồn từ phép tính Lambda. Currying lấy một hàm nhận nhiều hơn một tham số và chia thành thành một chuỗi các hàm đơn nhất (một tham số). Nói cách khác, một curried function chỉ nhận một tham số tại một thời điểm.
Ví dụ cơ bản về currying
function buildSandwich(ingredient1) {
return (ingredient2) => {
return (ingredient3) => {
return `${ingredient1},${ingredient2},${ingredient3}`
}
}
}
buildSandwich() trả về hàm khác - một hàm ẩn danh nhận đối số ingredient2. Sau đó, hàm này trả về hàm ẩn danh khác nhận ingredient3. Hàm cuối cùng trả về dạng chuỗi mẫu, một cách định dạng chuỗi trong JavaScript.
Những gì bạn vừa tạo là một hàm lồng nhau trong đó mỗi hàm sẽ gọi hàm bên dưới nó cho đến khi chúng ta kết thúc. Bây giờ, khi bạn gọi buildSandwich() và truyền cho nó một tham số duy nhất, nó sẽ trả về phần của hàm có đối số mà bạn chưa cung cấp:
console.log(buildSandwich("Bacon"))
Bạn có thể thấy kết quả buildSandwich trả về một hàm:
Để hoàn tất gọi hàm, bạn cần cung cấp tất cả 3 đối số:
buildSandwich("Bacon")("Lettuce")("Tomato")
Code này chuyển “Bacon” sang hàm đầu tiên, “Lettuce” sang thứ hai và “Tomato” sang hàm cuối cùng. Nói cách khác. Hàm buildSandwich() thực sự được chia thành 3 hàm, trong đó, mỗi hàm chỉ nhận một tham số.
Dù curry dùng các hàm truyền thống là hoàn toàn hợp lệ, tất cả hàm lồng nhau đều có thể trở nên xấu nếu bạn đi sâu hơn. Để khắc phục, bạn có thể dùng các hàm mũi tên và tận dụng cú pháp rõ ràng hơn.
const buildMeal = ingred1 => ingred2 => ingred3 =>
`${ingred1}, ${ingred2}. ${ingred3}`;
Phiên bản tái cấu trúc này chính xác hơn. Đây là lợi thế của việc dùng các hàm mũi tên so với hàm thông thường. Bạn có thể gọi hàm này theo cách đã thực hiện với hàm trước đó:
buildMeal("Bacon")("Lettuce")("Tomato")
Hàm curry được áp dụng một phần
Áp dụng một phần hàm là cách dùng phổ biến của curry. Kỹ thuật này đòi hỏi mỗi lần chỉ cung cấp các đối số cần thiết (thay vì cung cấp tất cả các đối số). Bất cứ khi nào bạn gọi một hàm bằng cách chuyển tất cả các tham số bắt buộc, bạn nói rằng bạn đã "áp dụng" hàm đó.
Ví dụ:
const multiply = (x, y) => x * y;
Bên dưới là phiên bản đã curry của phép nhân:
const curriedMultiply = x => y => x * y;
Hàm curriedMultiply() nhận đối số x cho hàm đầu tiên và y cho hàm thứ hai, sau đó nó nhân cả hai giá trị.
Để tạo hàm được áp dụng một phần đầu tiên, hãy gọi curriedMultiple() với tham số đầu tiên và gán hàm trả về cho một biến:
const timesTen = curriedMultiply(10)
Lúc này, code đã áp dụng một phần hàm curriedMultiply(). Vì thế, bất cứ khi nào bạn muốn gọi timesTen(), chỉ cần chuyển nó sang một số và số này sẽ tự động được nhân với 10:
console.log(timesTen(8)) // 80
Điều này cho phép bạn xây dựng một hàm phức tạp đơn lẻ bằng cách tạo nhiều hàm tùy biến từ nó, mỗi hàm có chức năng được khóa riêng.
Các hàm của JavaScript cực kỳ linh hoạt và currying chỉ là một phần nhỏ trong số đó. Hiện có nhiều kiểu hàm khác như mũi tên, xây dựng và ẩn danh. Tự mình làm quen với chúng và những thành phần liên quan là chìa khóa cho bạn làm chủ JavaScript.