Functional programming với javascript ES6

Funtional programming là kiểu xử lý các tính toán như đánh giá của các hàm toán học, tránh thay đổi state và dữ liệu mutable

Arrow function

Arrow function (Hàm dùng ký hiệu =>) giúp tạo các expression ngắn gọn, đóng gói một phần nhỏ chức năng. Ngoài ra, các arrow giữ phạm vi của phía gọi bên trong hàm loại bỏ nhu cầu self = this.

Ví dụ

const multiply = function(x,y) {
  return x * y;
}
// Có thể viết lại như sau
const multiply = (x, y) => { return x * y };
// Do hàm trả về mộ expression đơn nên không cần dấu đóng
const multiply = (x, y) => x * y;
console.log(multiply(5,10)) //50

Function Delegate

Function Delegate đóng gói một phương thức cho phép các hàm được tạo hoặc được truyền dưới dạng dữ liệu

Ví dụ

const isZero = n => n === 0;
const a = [0,1,0,3,4,0];
console.log(a.filter(isZero).length); // 3

Expression thay cho Statement

Các Statement định nghĩa một hành vi và được thực thi với side effect (tác nhân bên ngoài). Expression tạo ra kết quả với state không thay đổi ( no side effect)

Statement

const getSalutation = function(hour) {
  var salutation; // Giá trị temp
  if (hour < 12) {
    salutation = "Good Morning";
  } else {
    salutation = "Good Afternoon"
  }
  return salutation; // Gía trị mutable
}

Expression

const getSalutation = (hour) => hour < 12 ? "Good Morning" : "Good Afternoon";
console.log(getSalutation(10)); // Good Morning

Higher order function

Một hàm nhận hàm khác như một tham số hoặc trả về một hàm khác

Ví dụ

function mapConsecutive(values, fn) {
 let result = [];
 for(let i=0; i < values.length -1; i++) {
   result.push(fn(values[i], values[i+1]));
 }
 return result;
}

const letters = ['a','b','c','d','e','f','g'];

let twoByTwo = mapConsecutive(letters, (x,y) => [x,y]);
console.log(twoByTwo);
// [[a,b], [b,c], [c,d], [d,e], [e,f], [f,g]]

Currying

Currying cho phép một hàm có nhiều tham số được dịch sang thành một chuỗi các hàm. Hàm Currying có thể được điều chỉnh để phù hợp với chữ ký của hàm khác

Ví dụ

const convertUnits = (toUnit, factor, offset = 0) => 
  input => ((offset + input) * factor).toFixed(2).concat(toUnit);

const milesToKm = convertUnits('km', 1.60936, 0);
const poundsToKg = convertUnits('kg', 0.45460, 0);
const farenheitToCelsius = convertUnits('degrees C', 0.5556, -32);

milesToKm(10); //"16.09 km"
poundsToKg(2.5); //"1.14 kg"
farenheitToCelsius(98); //"36.67 degrees C"
const weightsInPounds = [5,15.4,9.8, 110];

// Không dùng currying
const weightsInKg = weightsInPounds.map(x => convertUnits('kg', 0.45460,
0)(x));
// dùng currying
const weightsInKg = weightsInPounds.map(poundsToKg);
// 2.27kg, 7.00kg, 4.46kg, 50.01kg

Các hàm tương tác với mảng

Các hàm mảng là cửa ngõ đến Functional programming trong Javascript. Các hàm này giúp các công việc xử lý ngắn gọn của hầu hết thói quen lập trình quen thuộc hoạt động trên mảng hoặc collection

  • [].every(fn) Kiểm tra tất cả các phần tử trong mảng thoả mãn điều kiện test
  • [].some(fn) | [].includes(fn) Kiểm tra phần tử bất kỳ trong mảng thoả mãn điều kiện test
  • [].find(fn) Trả về phần tử đầu tiên  trong mảng thoả mãn điều kiện test
  • [].filter(fn) trả về tất cả các phần tử trong mảng thoả mãn điều kiện test
  • [].map(fn) Trả về một mảng mới là kết qủa khi gọi một hàm nhận tất cả các phần tử của mảng như tham số đầu vào
  • [].reduce(fn(accumulator, currentValue)) Thực hiện một hàm được cung cấp cho mỗi phần tử của mảng (từ trái sang phải), trả về một giá trị duy nhất, accumulator
  • [].sort(fn(a,b)) – sử dụng mutable state, chỉnh sửa một mảng bằng cách sắp xếp các phần tử trong mảng. Một lựa chọn khác là sử dụng hàm so sánh để chỉnh sửa điều kiện sắp xếp
  • [...arr].sort() Dùng cách naỳ để sắp xếp tránh không dùng mutable
  • [].reverse() mutable state, đảo ngược thứ tự các phần tử trong mảng
  •  [...arr].reverse() đảo ngược thứ tự tránh không dùng mutable

Xâu chuỗi các phương thức

Xâu chuỗi các phương thức để cho phép một chuỗi các hàm gọi liên tiếp nhau mục đích để đạt được kết quả cuối cùng. Xâu chuỗi các phương thức cho phép thành phần hàm giống như một đường ống

Ví dụ

let cart = [
  {name: "Drink", price: 3.12},
  {name: "Steak", price: 45.15},
  {name: "Drink", price: 11.01}
];

let drinkTotal = cart.filter(x=> x.name === "Drink")
 .map(x=> x.price)
 .reduce((t,v) => t +=v)
 .toFixed(2);

console.log(Total Drink Cost $${drinkTotal}); // Total Drink Cost $14.13

Pipelines

Một Pipeline cho phép thành phần hàm dễ dàng khi thực hiện nhiều hoạt động khác nhau trên một biến. Vì Javascript thiếu một toán tử Pipeline, một design pattern có thể được sử dụng hoàn thành task

Ví dụ

const pipe = functions => data => {
  return functions.reduce( (value, func) => func(value), data);
};

let cart = [3.12, 45.15, 11.01];
const addSalesTax = (total, taxRate) => (total * taxRate) + total;

const tally = orders => pipe([
 x => x.reduce((total, val) => total + val), // tính tổng các order
 x => addSalesTax(x, 0.09),
 x => `Order Total = ${x.toFixed(2)}` // chuyển đổi thành text
])(orders);
// Order Total = 64.62

 

 

 

You May Also Like

About the Author: Nguyen Dinh Thuc

Leave a Reply

Your email address will not be published.