Ćwiczenie polega na napisaniu tzw. polyfilli dla wbudowanych w JavaScript (od pewnego czasu) metod tablicowych map, filter oraz forEach, które zakładam, że znamy przynajmniej pobieżnie.

Polyfill to funkcja, która odtwarza pewną nowość języka JavaScript i przypisuje ją do prototypu, np. jeżeli nowość dotyczy metody tablic, przypisujemy ją do prototypu tablicy.

W dawnych czasach polyfill był pisany po to, aby sprawdzić, czy dana przeglądarka internetowa obsługuje daną nowość, co nie zawsze było standardem, zaś ludzie też częściej posiadali starsze wersje przeglądarek, których nie aktualizowali.

Wtedy nasz kod sprawdzał, czy np. obiekt Array ma w prototypie forEach. Jeżeli nie ma, oznacza to, że dana przeglądarka nie obsługuje tej nowości. W takim wypadku do Array.prototype.forEach przypisywaliśmy nasz polyfill, dzięki czemu ktoś na starszej przeglądarce nie miał problemów z naszym kodem.

Dziś mamy wiele narzędzi do automatyzacji kodu, które robią to za nas i ogólnie pisanie polyfillów nie jest aż tak potrzebne – jest to swego rodzaju sztuka dla sztuki, która ma za zadanie:

  • sprawdzić, czy rozumiemy jak działają pewne metody JavaScript
  • wysilić nasze szare komórki i znajomość języka, aby napisać własne wersje tych metod

Napiszemy sobie własne, proste wersje tablicowych metod map, filter i forEach i przypiszemy je sobie do prototypu tablicy oraz wypróbujemy, dla ćwiczenia i lepszego poznania JavaScript.

Zanim to jednak zrobimy – przypomnimy sobie każdą funkcję i to, co chcemy osiągnąć.

Metoda map – przypomnienie

Metoda map służy do stworzenia tablicy w oparciu o już istniejącą tablicę. Przyjmuje funkcję, która jest wywoływana na każdym elemencie i zwraca ten element, odpowiednio przerobiony.

<script>
        let numbers = [1,2,3,4];
        let squared = numbers.map((num) => num * num);
        console.log(squared);
        //[1, 4, 9, 16]
    </script>

Tutaj mamy map na tablicy liczb. Funkcja callback przyjmuje jeden argument, element i zwraca element do kwadratu. Tak też wygląda nasza tablica – elementy z numbers podniesione do kwadratu.

Nasza metoda map – ćwiczenie

Tworzymy własną metodę map. Wiemy, że ma utworzyć nową tablicę, w oparciu o starą, przyjmując callback, który jest wywoływany na każdym elemencie tablicy:

Array.prototype.ourMap = function (callBack) {
            const newArray = [];
            for (let i = 0; i < this.length; i++) {
              newArray.push(callBack(this[i]));
            }
            return newArray;
          };

Do prototypu dopisaliśmy nasz własny map. Przyjmuje callback. Tworzy nową tablicę. Przechodzi po obecnej tablicy (this w tym wypadku wskazuje na tablicę, na której „po kropce” wywołujemy map) i do nowej zwraca element przepuszczony przez nasz callback.

Czy to działa?

<script>
        Array.prototype.ourMap = function (callBack) {
            const newArray = [];
            for (let i = 0; i < this.length; i++) {
              newArray.push(callBack(this[i]));
            }
            return newArray;
          };
        let numbers = [1,2,3,4];
        let squared = numbers.ourMap((num) => num * num);
        console.log(squared);
        //[1, 4, 9, 16]
    </script>

Działa.

Na spokojnie możemy sobie przeanalizować, co się tutaj dzieje. Słówko 'this’ odnosi się tutaj do 'numbers’, reszta powinna być prosta.

Metoda forEach – przypomnienie

Metoda forEach ma przyjąć jakąś funkcję, która przyjmuje element tablicy jako argument. I wywołać tę funkcję na każdym elemencie – od pierwszego, do ostatniego.

Tak wygląda forEach, który chcemy stworzyć:

<script>
        let numbers = [1,2,3,4];
        numbers.forEach((num) => console.log(num));
        //1
        //2
        //3
        //4
    </script>

Nasze forEach – ćwiczenie

Przypiszemy sobie funkcję do prototypu. Tak ona wygląda:

Array.prototype.ourForEach = function (callBack) {
  for (let i = 0; i < this.length; i++) {
    callBack(this[i]);
  }
};

Słówko 'this’ odnosi się do tablicy. Przyjmujemy callback. Idziemy w pętli i na każdym elemencie wywołujemy callback.

Zobaczmy, czy działa:

<script>
        Array.prototype.ourForEach = function (callBack) {
            for (let i = 0; i < this.length; i++) {
              callBack(this[i]);
            }
          };
        let numbers = [1,2,3,4];
        numbers.ourForEach((num) => console.log(num));
        //1
        //2
        //3
        //4
    </script>

Metoda filter – przypomnienie

Filter tworzy nową tablicę w oparciu o starą. Wrzucamy mu callback, który decyduje, czy dany element przechodzi czy nie:

<script>
        
        let numbers = [1,2,3,4];
        let even = numbers.filter((num) => num % 2 === 0);
        console.log(even);
        //[2, 4]
        
    </script>

Tutaj callback zwraca, że reszta z dzielenia przez 2 musi być równa 0. Do tablicy even przechodzą tylko liczby parzyste.

Nasz filter – ćwiczenie

Trochę musimy się nagimnastykować, aby to osiągnąć, ale w zasadzie nie jest to aż tak trudne:

Array.prototype.ourFilter = function (callBack) {
  let output = [];
  for (let i = 0; i < this.length; i++) {
    if (callBack(this[i])) {
      output.push(this[i]);
    }
  }
  return output;
};

Przechodzimy po każdym elemencie tablicy, jeżeli callback będzie prawdziwy, to ten element dodajemy do tablicy output, którą na koniec zwracamy.

Zobaczmy, czy działa:

<script>
        Array.prototype.ourFilter = function (callBack) {
            let output = [];
            for (let i = 0; i < this.length; i++) {
              if (callBack(this[i])) {
                output.push(this[i]);
              }
            }
            return output;
          };
        let numbers = [1,2,3,4];
        let even = numbers.ourFilter((num) => num % 2 === 0);
        console.log(even);
        //[2, 4]
        
    </script>

Działa bez problemu.