Đối với mỗi lập trình viên, việc viết một đoạn mã có thể sử dụng lại nhiều lần là điều luôn cần thiết. Mỗi người có những cách để hiện thực hoá chúng, ví như việc đóng thư viện các đoạn mã hữu ích chẳng hạn. Nhưng trong bài viết này chúng ta hãy nói ở tầm vi mô hơn đó là ở mức độ hàm.
Khái niệm về curry function khá là đơn giản, nó chính là một hàm javascript bình thường trong đó nhận nhiều đối số bằng một loạt các hàm một đối số. Ví dụ:
function sum(a) {
return function add(b) {
return a + b;
}
}
const add1 = sum(1);
const result = add1(2);
console.log(result); // 3;
// Hoặc ngắn gọn hơn chúng ta cũng có thể viết:
const result = sum(1)(2);
console.log(result); // 3
Oh, cú pháp có vẻ lơi lạ lẫm ha. Vậy thì điều này mang lại lợi ích gì?
Có 2 điều mà curry function có thể mang lại:
Hãy để tôi ví dụ cho các bạn làm rõ 2 điều trên.
Ví dụ đầu tiên tôi có một mảng các đối tượng gồm có id và name, hãy lấy ra chỉ mỗi id:
const objects = [{ id: 1, name: "A" }, { id: 2, name: "B" }, { id: 3, name: "C" }];
const ids = objects.map(function (o) { return o.id; });
Thật đơn giản phải không? Nhưng có một vấn để ở đây function (o) { return o.id; }
là đủ nhưng chưa tốt. Hãy làm cho nó tốt hơn:
function get(attr) {
return function getAttr(obj) {
return obj[attr];
}
}
const ids = objects.map(get("id"));
get là một hàm curry, nó nhận vào một attr
để phục vụ việc việc lấy ra attribute nào và một obj
là đối tượng cần được lấy ra.
Có thể bạn sẽ cảm thấy hơi khó hiểu với cách viết ở ids
kia, hãy nhớ lại hàm map
của javascript một chút:
Array.map(function (i) { return i; });
.map
nhận vào tham số là một hàm, hàm này có quyền truy cập vào mỗi phần tử trong mảng để xử lý.
Hay nói cách khác đây thực sự là những gì mà ids
được làm:
const ids = objects.map(function (o) { return get("id")(o); });
Đến đây thì các bạn đã hiểu rồi chứ? Vậy thì tôi có một câu hỏi dành cho bạn: nếu như tôi muốn dùng lại hàm "lấy id ra khỏi objects" kia nhiều lần thì sẽ làm như thế nào? Có phải là như thế này:
function getIds(objs) {
return objects.map(get("id"));
}
getIds(objects);
Chà có vẻ mọi thứ hoạt động, tuy nhiên nó vẫn có thể tốt hơn. Tại sao chúng ta không áp dụng curry function để tạo ra một hàm được gọi là "map" có tác dụng lặp qua các phần tử trong mảng?
function map(func) {
return function exec(value) {
return value.map(func);
}
}
function getIds = map(get("id"));
getIds(objects);
Đến đây thì tôi thấy rằng, nếu các hàm cơ bản là curry thì tôi có thể dễ dàng tạo ra một hàm thực hiện một chức năng mới từ chúng. Và một điều nữa là đoạn mã được viết rất dễ đọc, nhìn vào biết ngay là làm gì.
Một ưu điểm của curry function là nó cho phép mã của bạn tập trung vào chức năng của nó hơn là một hàm thông thường. Như ở các ví dụ trên, việc tạo ra các hàm map
, get
, getIds
giúp các đoạn mã dễ đọc hơn vì nó phản ánh đúng nhiệm vụ mà nó sẽ làm.
Hãy xem xét một ví dụ: Tôi có một hàm lấy dữ liệu JSON từ một máy chủ là fetchData
, đây là một hàm bất đồng bộ và sau khi nhận được phản hồi, tôi sẽ lấy ra posts và danh sách id của các posts.
fetchData()
.then(JSON.parse)
.then(function(data){ return data.posts })
.then(function(posts){
return posts.map(function(post){ return post.title })
});
Có thể bạn sẽ có cách viết khác tôi, nhưng trong trường hợp này tôi muốn nhấn mạnh việc sử dụng curry function sẽ làm đoạn mã kia trông dễ hiểu hơn:
fetchData()
.then(JSON.parse)
.then(get("posts")
.then(map(get("id")));
Vì lợi ích mang lại của curry function, cũng có khá là nhiều những thư viện hỗ trợ như curry, hàm curry của Ramda là một thư viện nổi tiếng cho lập trình hàm.
Curry function là một cách để "chơi" với hàm, diễn dãi hàm một cách dễ hiểu với những lợi ích mà nó mang lại. Ngay từ bây giờ nếu bạn cảm thấy hứng thú thì hãy thử áp dụng nó vào trong dự án tiếp theo của mình.
5 bài học sâu sắc
Mỗi sản phẩm đi kèm với những câu chuyện. Thành công của người khác là nguồn cảm hứng cho nhiều người theo sau. 5 bài học rút ra được đã thay đổi con người tôi mãi mãi. Còn bạn? Hãy bấm vào ngay!
Đăng ký nhận thông báo bài viết mới
Bình luận (0)