30.05.20195 min

Riccardo PolacciFrontend Developer

Wydajność kontra czytelność w JavaScript

Dowiedz się, kiedy warto postawić na wydajność, a kiedy na czytelność kodu i czy da się pogodzić te dwa podejścia w JavaScript oraz kiedy zdecydować się na użycie ES6.

Wydajność kontra czytelność w JavaScript

JavaScript ewoluuje w coraz to bardziej czytelny język. Nie ma co do tego wątpliwości i ani powodu do żadnych obaw. Rozwój oprogramowania to dynamiczny rynek, na którym zespoły nieustannie się zmieniają, co oznacza, że ​​kod musi być czytelny dla nowych użytkowników. Ale czy dzieje się to kosztem wydajności? Gdzie wyznaczyć granicę między wydajnością a czytelnością? Kiedy powinniśmy poświęcić jedno czy drugie? Czy w ogóle musimy coś poświęcać?

Oto niektóre z pytań, na które chciałbym dziś odpowiedzieć lub przynajmniej spróbować wspólnie je zrozumieć. Powody, dla których staramy się osiągnąć wysoki poziom wydajności w kodzie, powinny być oczywiste. Ale dlaczego mamy taką obsesję na punkcie czytelności?


Ten sam problem, różne rozwiązania

Cóż, jakiś czas temu bardzo często mogliśmy zobaczyć podobny problem:

Zaczynając od tablicy liczb nieuporządkowanych, zwróć nową tablicę, dodając 1 do każdej wartości i sortując ją bez mutowania oryginału:

var numbers = [2, 4, 12, 6, 8, 29, 5, 10, 87, 11, 7];

function process(arr) {
    let newArr = arr.slice();
    newArr[0]++;
    for (let i = 1; i < newArr.length; i++) {
        const current = newArr[i] + 1;
        let leftIndex = i - 1;

        while (leftIndex >= 0 && newArr[leftIndex] > current) {
            newArr[leftIndex + 1] = newArr[leftIndex];
            leftIndex = leftIndex - 1;
        }
        newArr[leftIndex + 1] = current;
    }
    return newArr;
}

const newArray = process(numbers);


(używam sortowania przez wstawianie tylko dlatego, że łatwiej było go zaimplementować). Ten przykładowy kod nie jest tak naprawdę czytelny, ale jest wydajny i to dużo bardziej niż czytelny kod ES6, np. taki:

const process = (arr) => arr
    .map(num => num + 1)
    .sort((a, b) => a - b);

const newArray = process(numbers);


W rzeczywistości pierwszy kawałek kodu jest ~ 75% szybszy niż drugi, mimo że drugi jest bardziej czytelny i może nawet zostać uproszczony do jednego wiersza:

const newArray = numbers.map(num => num + 1).sort((a, b) => a - b);


Lub podzielona na funkcje pomocnicze dla lepszej czytelności:

const addOne = (n) => n + 1;
const asc = (a, b) => a - b;
const newArray = numbers.map(addOne).sort(asc);


Jest oczywiste, że kod ES6 (niezależnie od podejścia) jest dużo bardziej czytelny, dzięki czemu jest łatwiejszy do zrozumienia na pierwszy rzut oka. Dzięki czytelnemu kodowi, możemy szybciej wprowadzać nowych deweloperów do projektów, możemy łatwiej dzielić się naszym kodem i staje się on łatwiejszy do utrzymania.

Biorąc wszystko pod uwagę, wydajność w większości przypadków staje się zbędna. Właśnie dlatego ES6 ewoluował w ten sposób.

Ostateczne porównanie obu podejść:

źródło

W tym momencie prawdopodobnie zastanawiasz się: Co jeszcze jest mniej wydajne, ale bardziej czytelne?

Cóż, spójrzmy razem na kilka przypadków użycia.

Składnia rozwinięcia vs Object.assign()

Zacznijmy od następującego prostego problemu:
Skopiuj obiekt i dodaj nową właściwość do kopii

Rozwiązania:

const params = {...}; // filled Object

// ES6 - Spread syntax
var copy1 = { a: 2, ...params };

// Object.assign()
var copy2 = Object.assign({}, { a: 2 }, params);


Oba te podejścia wykonują zadanie, ale wszyscy możemy się zgodzić, że składnia rozwinięcia jest bardziej czytelna mimo że jest ~ 54% wolniejsza.

źródło

For loop vs Reduce

Problem:
Zsumuj wszystkie wartości z tablicy.

Rozwiązania, zacznijmy od klasycznego … loop:

const numbers = [2, 4, 12, 6, 8, 29, 5, 10, 87, 11, 7];

function arraySum(arr) {
    let sum = 0;
    for (let i = 0; i < arr.length; i++) {
        sum += arr[i]
    }
    return sum;
}

const sumOfNumbers = arraySum(numbers);


Przejdźmy teraz do wszechpotężnego reduce:

const numbers = [2, 4, 12, 6, 8, 29, 5, 10, 87, 11, 7];

const add = (a, b) => a + b;
const arraySum = (arr) => arr.reduce(add);

const sumOfNumbers = arraySum(numbers);


W tym przypadku reduce jest niezwykle kosztowny z punktu widzenia wydajności - jest ~ 96% wolniejszy!

źródło

For vs While vs Do while

źródło

Różnica jest prawie niezauważalna, ale mimo to w przypadku wątpliwości… wybierz klasycznego for loop.


Kiedy korzystać z czego?

Wow! Dobre sobie… Używam składni rozwinięcia, reduce itd. dla wszystkich moich operacji! Szczerze mówiąc, to trochę przygnębiające - obiecali mi czytelność bez utraty wydajności! Żądam spowrotem moich pieniędzy! (tryb paniki)

Nie panikujmy i przeanalizujmy sytuację. Więc kiedy powinienem czego użyć? Odpowiedź na powyższe pytanie jest prostsza, niż oczekiwałeś: Zależy.

Wracając do pierwszego przykładu, jeśli musimy: kopiować, dodawać i sortować macierz lub obiekt małych lub średnich rozmiarów… Następnie chcemy celować w czytelność, powinniśmy wykorzystać wszystkie dostępne zabawki z arsenału ES6.

W rzeczywistości prawie cały nasz kod można przepisać, koncentrując się na czytelności zamiast wydajności, oczywiście w zależności od projektu.

Spróbujmy zrobić listę.

Kiedy priorytetem powinna być czytelność

  • Gdy dane, z którymi mamy do czynienia, nie są zbyt duże,
  • Gdy aplikacja działa poprawnie pod względem szybkości, obciążenia itp.,
  • Podczas pracy w dynamicznym środowisku z wieloma nowicjuszami w projekcie,
  • Pisząc bibliotekę lub wtyczkę, która wymaga przeczytania dla zrozumienia.



Kiedy priorytetem powinna być wydajność

  • Gdy mamy do czynienia z dużymi danymi,
  • Gdy aplikacja jest powolna lub ma inne problemy z wydajnością,
  • Gdy projekt ma być skalowalny,
  • Podczas pracy nad projektem osobistym, koduj jak chcesz.


Więc jeśli chodzi o duże dane, unikaj stosowania reduce, filter, map, składni rozwinięcia itp. w tej części kodu, która dotyczy konkretnie tego obiektu lub tablicy.


Wnioski

Zamiast rzucać się od razu na najfajniejsze i najnowsze rozwiązania, powinniśmy cofnąć się o krok i przeanalizować, czy jest to wygodne dla naszego projektu i dla naszego przypadku.

Niewątpliwie nowe funkcje ES6 są błogosławieństwem i sprawiają, że codzienne kodowanie z JavaScript jest przyjemnością, ale jeśli zmagamy się z wydajnością, jeśli obsługujemy duże ilości danych... Powinniśmy ponownie przemyśleć, których narzędzi używać.

Przy klasie wagi ciężkiej wybieram mniej czytelny, ale wydajniejszy kod ? W przypadku ogromnych danych, wybieram staranny research i wdrożenie najbardziej wydajnych algorytmów dla tego zadania. We wszystkich innych przypadkach, wybieram piękną czytelność ES6! ❤

Zastrzeżenie

Wyniki testu pokazane w tym poście mogą się nieznacznie różnić w zależności od obciążenia przeglądarki, systemu operacyjnego i serwera.

<p>Loading...</p>

Powiązane artykuły

Dziel się wiedzą ze 160 tysiącami naszych czytelników

Zostań autorem Readme

Ubezpieczeniowy Fundusz Gwarancyjny

Specjalista ds. testów

medium

Znamy widełki

Kontrakt B2BUmowa o pracę

Warszawa

Ważna do 27.02.2022

Dobrze
JIRASoapUIOracle Database
Początkujący
FitNesseJMeter

Sii Polska

DevOps Engineer

medium

17 000 - 24 000 PLN

Kontrakt B2B

Warszawa

Ważna do 27.02.2022

Dobrze
AWSGCPGitHub
Bardzo dobrze
Linux

Accenture Polska

Agile Project Manager

medium

Brak widełek

Kontrakt B2BUmowa o pracę

Warszawa

Praca zdalna 100%

Ważna do 27.02.2022

Bardzo dobrze
Agile Methodologies (Scrum, Kanban, Kaizen)
Dobrze
Team-building & people management
Początkujący
cloud fundamentals

DB Schenker Technology Center Warsaw

Business Analyst

medium

Brak widełek

Kontrakt B2BUmowa o pracę

Warszawa

Ważna do 27.02.2022

Accenture Polska

PHP/Magento Developer

medium

Brak widełek

Kontrakt B2BUmowa o pracę

Praca zdalna 100%

Ważna do 27.02.2022

Bardzo dobrze
PHPGIT
Dobrze
Magento 2SOAP/REST HTML/CSS
Początkujący
JSONJavaScript jQuery

T-Mobile Polska S. A.

Inżynier Sieciowy

medium

Brak widełek

Kontrakt B2B

Warszawa

Ważna do 27.02.2022

Asseco Poland S.A.

Młodszy Analityk Finansowy

junior

Brak widełek

Umowa o pracę

Rzeszów

Ważna do 27.02.2022

Netguru

Junior iOS Developer

junior

4 200 - 6 000 PLN

Kontrakt B2BUmowa o pracę

Praca zdalna 100%

Ważna do 27.02.2022

Dobrze
iOS

7N

Data Engineer

medium

15 100 - 18 100 PLN

Kontrakt B2B

Praca zdalna 100%

Ważna do 27.02.2022

Dobrze
MS SQLETLSpark

7N

Senior Business Analyst

senior

16 800 - 21 800 PLN

Kontrakt B2B

Praca zdalna 100%

Ważna do 27.02.2022