Podstawowe pojęcia w programowaniu funkcjonalnym za pomocą JavaScript

Mar 12 2023
Programowanie funkcyjne to paradygmat, który zyskał popularność w ostatnich latach ze względu na możliwość pisania bardziej modułowego, wielokrotnego użytku i łatwego w utrzymaniu kodu. JavaScript, będąc wszechstronnym językiem programowania, dobrze nadaje się również do programowania funkcjonalnego.
Koncepcje programowania funkcjonalnego w JavaScript

Programowanie funkcyjne to paradygmat, który zyskał popularność w ostatnich latach ze względu na możliwość pisania bardziej modułowego, wielokrotnego użytku i łatwego w utrzymaniu kodu. JavaScript, będąc wszechstronnym językiem programowania, dobrze nadaje się również do programowania funkcjonalnego. W tym artykule przyjrzymy się podstawowym koncepcjom programowania funkcjonalnego za pomocą JavaScript i wyjaśnimy, w jaki sposób mogą one pomóc w pisaniu lepszego kodu:

  1. Pełni funkcję obywateli pierwszej klasy
  2. Funkcje wyższego rzędu
  3. Czyste funkcje
  4. Niezmienność
  5. rekursja

W programowaniu funkcyjnym funkcje są traktowane jako obywatele pierwszej klasy, co oznacza, że ​​można je przypisywać do zmiennych, przekazywać jako argumenty do innych funkcji i zwracać jako wartości. Ta koncepcja jest kluczowa w programowaniu funkcjonalnym, ponieważ pozwala nam pisać bardziej modułowy i wielokrotnego użytku kod.

Oto kilka przykładów tego, jak funkcje są traktowane jako obywatele pierwszej klasy w JavaScript:

  1. Przypisywanie funkcji do zmiennych:

const add = function(x, y) {
  return x + y;
}

2. Przekazywanie funkcji jako argumentów:

Funkcje mogą być również przekazywane jako argumenty do innych funkcji. Pozwala to na bardziej modułowe podejście do programowania, ponieważ pozwala nam ponownie wykorzystać tę samą funkcję z różnymi wejściami i wyjściami. Na przykład:

function greet(name, callback) {
  const message = "Hello, " + name + "!";
  callback(message);
}

function logMessage(message) {
  console.log(message);
}

greet("Sumit", logMessage); // Logs "Hello, Sumit!"

3. Zwracanie funkcji jako wartości:

Funkcje mogą być również zwracane jako wartości z innych funkcji. Pozwala to na bardziej zaawansowane techniki, takie jak zamknięcia i curry. Na przykład:

function add(x) {
  return function(y) {
    return x + y;
  };
}

const addFive = add(5);
console.log(addFive(3)); // Logs 8

✓ Funkcje wyższego rzędu

Funkcje wyższego rzędu to funkcje, które przyjmują inne funkcje jako argumenty lub zwracają funkcje jako wartości. Pozwalają nam pisać bardziej abstrakcyjny i uniwersalny kod, który można zastosować w różnych przypadkach użycia. Niektóre popularne funkcje wyższego rzędu w JavaScript to map(), filter() i reduce().

Oto kilka przykładów funkcji wyższego rzędu w JavaScript:

  1. Tablica.prototyp.map():

const numbers = [1, 2, 3, 4];
const doubled = numbers.map(function(num) {
  return num * 2;
});
console.log(doubled); // Logs [2, 4, 6, 8]

2. Tablica.prototyp.filtr():

Metoda filter()jest kolejną funkcją wyższego rzędu, która jako argument przyjmuje funkcję wywołania zwrotnego i zwraca nową tablicę zawierającą tylko te elementy, które pomyślnie przeszły test określony w wywołaniu zwrotnym. Na przykład:

const numbers = [1, 2, 3, 4];
const even = numbers.filter(function(num) {
  return num % 2 === 0;
});
console.log(even); // Logs [2, 4]

3. Funkcja.prototyp.bind():

Metoda bind()jest funkcją wyższego rzędu, która zwraca nową funkcję z określoną thiswartością i początkowymi argumentami. Jest często używany do tworzenia nowej funkcji z określonym kontekstem lub do tworzenia częściowo zastosowanej funkcji. Na przykład:

const person = {
  firstName: "Sumit",
  lastName: "Saha",
  fullName: function() {
    return this.firstName + " " + this.lastName;
  }
};

const logName = function(greeting) {
  console.log(greeting + " " + this.fullName());
};

const boundLogName = logName.bind(person, "Hello");
boundLogName(); // Logs "Hello Sumit Saha"

To tylko kilka przykładów z wielu funkcji wyższego rzędu dostępnych w JavaScript. Korzystając z funkcji wyższego rzędu, możesz napisać bardziej ogólny i wielokrotnego użytku kod, który można zastosować w różnych przypadkach użycia.

✓ Czyste funkcje

Czyste funkcje to funkcje, które nie mają skutków ubocznych, co oznacza, że ​​nie modyfikują żadnych zewnętrznych stanów ani zmiennych poza swoim zakresem. Zawsze zwracają to samo wyjście dla tego samego wejścia, co czyni je deterministycznymi. Czyste funkcje są niezbędne w programowaniu funkcjonalnym, ponieważ promują przewidywalność kodu, testowalność i łatwość konserwacji.

Oto kilka przykładów czystych funkcji w JavaScript:

  1. Funkcja dodawania:
  2. function add(a, b) {
      return a + b;
    }
    

function concatenateArrays(arr1, arr2) {
  return arr1.concat(arr2);
}

3. Funkcja formatowania daty:

function formatDate(date) {
  const year = date.getFullYear();
  const month = date.getMonth() + 1;
  const day = date.getDate();
  return `${year}-${month}-${day}`;
}

4. Funkcja wielkich liter ciągu:

function capitalize(str) {
  return str.charAt(0).toUpperCase() + str.slice(1);
}

Ogólnie rzecz biorąc, funkcje czyste to funkcje, które nie mają żadnych skutków ubocznych i zawsze zwracają ten sam wynik dla tego samego wejścia. Są przewidywalne i łatwiejsze do uzasadnienia, co czyni je idealnymi do pisania łatwego w utrzymaniu i wielokrotnego użytku kodu.

✓ Niezmienność

Niezmienność to koncepcja polegająca na unikaniu modyfikowania struktur danych lub zmiennych po ich utworzeniu. Zamiast zmieniać obiekt lub tablicę, tworzymy nowy z pożądanymi zmianami. Takie podejście zapobiega niezamierzonym skutkom ubocznym i sprawia, że ​​kod jest bardziej przewidywalny i łatwiejszy do uzasadnienia.

Oto kilka przykładów niezmienności w JavaScript:

  1. Używanie operatora spread do tworzenia nowej tablicy lub obiektu:
  2. const arr1 = [1, 2, 3];
    const arr2 = [...arr1, 4];
    // arr1 is still [1, 2, 3], arr2 is [1, 2, 3, 4]
    
    const obj1 = { a: 1, b: 2 };
    const obj2 = { ...obj1, c: 3 };
    // obj1 is still { a: 1, b: 2 }, obj2 is { a: 1, b: 2, c: 3 }
    

const arr1 = [1, 2, 3];
const arr2 = arr1.concat(4);
// arr1 is still [1, 2, 3], arr2 is [1, 2, 3, 4]

3. Używając Object.assign()do tworzenia nowego obiektu:

const obj1 = { a: 1, b: 2 };
const obj2 = Object.assign({}, obj1, { c: 3 });
// obj1 is still { a: 1, b: 2 }, obj2 is { a: 1, b: 2, c: 3 }

4. Używanie Object.freeze()w celu uniemożliwienia modyfikacji obiektu:

const obj1 = Object.freeze({ a: 1, b: 2 });
obj1.c = 3;
// obj1 is still { a: 1, b: 2 }, the assignment is ignored

Ogólnie rzecz biorąc, niezmienność oznacza, że ​​dane nie są zmieniane bezpośrednio, ale raczej tworzone są nowe kopie po wprowadzeniu zmian. Może to pomóc w zapobieganiu niezamierzonym skutkom ubocznym i sprawić, że kod będzie bardziej przewidywalny i łatwiejszy w utrzymaniu.

✓ Rekurencja

Rekurencja to technika, w której funkcja wywołuje samą siebie w celu rozwiązania problemu. Jest potężnym narzędziem w programowaniu funkcjonalnym, ponieważ pozwala rozwiązywać złożone problemy, dzieląc je na mniejsze, prostsze. Jednak rekurencja może być trudna w użyciu, ponieważ może prowadzić do błędów przepełnienia stosu i problemów z wydajnością, jeśli nie jest używana prawidłowo.

Oto kilka przykładów rekurencji w JavaScript:

  1. Funkcja czynnikowa:
  2. function factorial(n) {
      if (n === 0) {
        return 1;
      } else {
        return n * factorial(n - 1);
      }
    }
    
    factorial(5); // returns 120 (5! = 5 * 4 * 3 * 2 * 1)
    

function sum(arr) {
  if (arr.length === 0) {
    return 0;
  } else {
    return arr[0] + sum(arr.slice(1));
  }
}

sum([1, 2, 3, 4, 5]); // returns 15 (1 + 2 + 3 + 4 + 5)

3. Funkcja Fibonacciego:

function fibonacci(n) {
  if (n === 0 || n === 1) {
    return n;
  } else {
    return fibonacci(n - 1) + fibonacci(n - 2);
  }
}

fibonacci(6); // returns 8 (0, 1, 1, 2, 3, 5, 8)

Rekurencja może być potężnym narzędziem w rozwiązywaniu problemów obejmujących powtarzające się wzorce lub struktury. Jednak może to również prowadzić do problemów z wydajnością i błędów przepełnienia stosu, jeśli nie zostanie starannie zaimplementowane. Ważne jest, aby zrozumieć koncepcję rekurencji i rozsądnie używać jej w swoim kodzie.

Wniosek

Podsumowując, programowanie funkcyjne jest ważnym paradygmatem, który może pomóc w pisaniu bardziej modułowego, wielokrotnego użytku i łatwego w utrzymaniu kodu. Dzięki zrozumieniu podstawowych pojęć, takich jak funkcje jako obywatele pierwszej klasy, funkcje wyższego rzędu, funkcje czyste, niezmienność i rekurencja, możesz zacząć pisać bardziej funkcjonalny kod w JavaScript. Programowanie funkcyjne to nie jedyny sposób na napisanie dobrego kodu, ale jest to potężne narzędzie, które może pomóc w rozwiązywaniu złożonych problemów i uczynieniu kodu bardziej przewidywalnym i łatwiejszym do uzasadnienia.