Вопрос: насколько это правильное решение? Не замедляю ли я таким образом код? Ведь я заставляю его постоянно вызывать функцию. Может быть это как-то сказывается на ОЗУ?
Если функция вызывается не сотни/тысячи раз в секунду, то не критично. Такие микропотимизации не должны плохо влиять на качество кода. Разница скорости выполнения будет в пределах погрешности.
Почему раньше, когда я видела js код, постоянно писали getElementById или getElementsByClassName
Так делали тогда, когда была плохая поддержка браузерами. И даже на данный момент с ней есть проблемы, например, NodeList который querySelectorAll возвращает, в старых браузерах не имеет метод forEach. Поэтому часто добавляют полифил или преобразуют NodeList в массив, который уже имеет forEach.
Подскажите, как быть с функцией, которая бы делала forEach?
Функция должна принимать либо другую функцию
qsf('.elements', el => el.classList.add("red"));
либо она должна внутри себя в замыкании хранить ссылку на список элементов, и возвращать публичные методы, которые будут производить операции над этими элементами (как такое написать, попытайтесь сами разобраться, если проходили тему с замыканиями).
qsf('.elements').addClass('class-name');