Введение

Есть много подходов к одной проблеме, и мы всегда ищем наиболее эффективное решение. Сегодня давайте сравним эффективность итерации и рекурсии.

Давайте рассмотрим простую проблему - для данного массива строк напишите функцию, которая будет использовать заглавные буквы для каждого элемента массива.

Я знаю, что все клетки вашего мозга начали прыгать, чтобы решить эту проблему. Да, у всех у нас будет разный подход к этому. Мне пришло в голову следующее. Просто и коротко.

let capArr1 = (arr) => {
    return arr.map((val) => val.toUpperCase());
}
capArr1(["Wolverine", "Ironman"]); //["WOLVERINE", "IRONMAN"]

Итерация

Большинство из нас согласятся, что прямым решением этого было бы использование цикла и перебора элементов с заглавными буквами. Я намеренно использовал здесь новую переменную.

let capArr2 = (arr) => {
    let newArr = [];
    for (let i = 0; i < arr.length; i++) {
        newArr.push(arr[i].toUpperCase());
    }
    return newArr;
}
capArr2(["Wolverine", "Ironman"]); //["WOLVERINE", "IRONMAN"]

Рекурсия

Некоторые заинтригованные умы хотели бы использовать рекурсию, чтобы решить эту проблему. Теперь вы, вероятно, знаете, почему я использовал новую переменную для хранения своего массива, поскольку я хочу, чтобы мои операции были одинаковыми в обоих подходах, чтобы сравнить время, затрачиваемое на выполнение этих функций.

let capArr3 = (arr) => {
    if (arr.length === 1) {
        return [arr[0].toUpperCase()];
    }
    let newArr = capArr3(arr.slice(0, -1));
    newArr.push(arr.slice(-1)[0].toUpperCase());
    return newArr;
}
capArr3(["Wolverine", "Ironman"]); //["WOLVERINE", "IRONMAN"]

Давайте воспользуемся функцией performance.now (), чтобы измерить время, затрачиваемое каждой функцией, и увеличим размер входного массива, чтобы иметь возможность идентифицировать заметную разницу.

let veryLongArr = ["wolverine", "ironman", "captain america", "thor", "hulk", "black widow", "hawkeye", "black panther", "vision", "dr.strange", "antMan", "wasp", "spiderman", "scarlett witch", "quicksilver", "grrot", "star lord", "gamora", "rocket raccoon", "drax", "ms.marvel"] and so on.

Я создал массив из 100,00 элементов, многократно повторяя эти значения. Фух! Ну не совсем, мне помогла функция Array concat. Я создал новую функцию, которая примет одну из трех вышеперечисленных функций и найдет среднее время для выполнения 10 функций.

let avgTime = (func, veryLongArr) => {
    let timeArr = [],
        n = 0;
    while (n < 10) {
        let t1 = performance.now();
        func(veryLongArr);
        let t2 = performance.now();
        timeArr.push(t2 - t1);
        n++;
    }
    console.log(timeArr);
    return (timeArr.reduce((acc, val) => acc + val)) / 10
}
avgTime(capArr1, veryLongArr);
avgTime(capArr2, veryLongArr);
avgTime(capArr3, veryLongArr);

Последние мысли

карта и для почти сопоставимы. Небольшая разница в производительности наблюдается для огромного набора данных. карта для меня выглядит чище и является отличным выбором, если мы уверены, что будем перебирать весь набор данных в прямом порядке, тогда как в для вы можете влиять на порядок и значение приращения.

С другой стороны, Рекурсия требует памяти, так как каждый раз она помещает адрес памяти в стек вызовов. Так что из соображений производительности лучше не использовать рекурсию. Мы можем использовать рекурсию, чтобы сделать решение более простым и читаемым.

Я бы посоветовал писать код таким образом, чтобы он был читаемым и понятным для парня, который будет поддерживать ваш код в ближайшие годы. Надеюсь, это было проницательно.

До скорого! :)