Currying trong JavaScript như thế nào?

559
18-09-2024
Currying trong JavaScript như thế nào?

Currying là gì?

Currying là một kỹ thuật chuyển đổi hàm có nhiều đối số (arity) thành chuỗi nhiều hàm có một đối số (arity) cho mỗi hàm.

Trong JavaScript, Currying là một kỹ thuật biến đổi hàm nhận nhiều tham số thành một chuỗi các hàm, mỗi hàm chỉ nhận một tham số duy nhất. Ví dụ, Currying sẽ chuyển đổi hàm foo(a, b, c) thành curriedFoo(a)(b)(c).

Vậy làm thế nào để hiện thực hóa Currying trong JavaScript? Đoạn mã dưới đây minh họa cách thức hoạt động của một hàm Currying:

/**  * Hàm này sẽ thực hiện Currying trên hàm được truyền vào.  */ function currying(functionToBeCurried) {   return function curriedFunction(...args) {     if (args.length >= functionToBeCurried.length) {       return functionToBeCurried.apply(this, args)     } else {       return function (...args2) {         return curriedFunction.apply(this, args.concat(args2))       }     }   } } 

Dòng mã trên hoạt động theo nguyên tắc: nếu số lượng tham số được cung cấp bằng hoặc lớn hơn số lượng tham số của hàm gốc, hàm gốc sẽ được gọi. Ngược lại, một hàm mới sẽ được trả về và tiếp tục được gọi đệ quy cho đến khi nhận đủ số lượng tham số còn thiếu.

Để minh họa rõ hơn, chúng ta hãy cùng xem xét ví dụ về hàm printBill() được áp dụng Currying:

const prices = {   "Product 1": 10,   "Product 2": 20,   "Product 3": 30, };  /**  * Hàm này sẽ được áp dụng Currying.  */ function printBill(date, productName, quantity) {   let headerString = "Date     Product    Total";   let message = `${date.toDateString()}  ${productName}   ${quantity * prices[productName]}`;   console.log(headerString);   console.log(message);   console.log();   console.log();   }  // Phiên bản Curried của hàm printBill. let curriedPrintBill = currying(printBill); 

Chúng ta có thể gọi cả hàm gốc và hàm đã được áp dụng Currying như sau:

const today = new Date(); console.log("Kết quả khi gọi hàm gốc..."); printBill(today, "Product 1", 10); // Hàm gốc. console.log("Kết quả khi gọi hàm Curried với đầy đủ tham số..."); curriedPrintBill(today, "Product 1", 10); // Hàm Curried với đầy đủ tham số. console.log("Kết quả khi gọi hàm Curried với một tham số..."); curriedPrintBill(today)("Product 1")(10); // Hàm Curried với một tham số. console.log("Kết quả khi gọi hàm Curried với nhiều tham số..."); curriedPrintBill(today)("Product 1", 10); // Hàm Curried với nhiều tham số. 

Một trong những ứng dụng hữu ích của Currying là tạo ra các “hàm áp dụng một phần” (Partially Applied Function) hay còn gọi là "partial". Hàm áp dụng một phần là phiên bản Curried của hàm gốc với một số tham số đã được cố định.

Ví dụ, tất cả các phiên bản "partial" dưới đây sẽ tạo ra cùng một kết quả:

// Tạo 'hàm áp dụng một phần' với ngày hiện tại. // Tham số đầu tiên của 'printTodaysBill()' được cố định là ngày hiện tại. let printTodaysBill = curriedPrintBill(today); console.log("Kết quả khi gọi 'hàm áp dụng một phần' với tham số 'Date' đã được cố định."); printTodaysBill("Product 1", 10);  // Một 'hàm áp dụng một phần' khác với ngày hiện tại và "Product 1" là các tham số cố định. let printTodaysBillForProduct1 = curriedPrintBill(today)("Product 1"); console.log(   "Kết quả khi gọi 'hàm áp dụng một phần' với ngày hiện tại và 'Product 1' là các tham số cố định.", ); printTodaysBillForProduct1(10); 

Currying mang lại nhiều lợi ích trong lập trình JavaScript, giúp code trở nên linh hoạt và dễ đọc hơn. Bạn có thể tham khảo thêm ví dụ trực quan về Currying tại JSFiddle: [JavaScript Currying JSFiddle].


SHARE