Tem vantagens e desvantagens em fazer quaisquer dos dois e muitas vezes é mais questão de gosto.
Uma grande desvantagem do jeito mais declarativo é que em problemas complexos pode ficar bem complicado fazer, até por não ter tanto controle de como o fluxo anda, por exemplo quando tem uma situação de break
. E quando muda o estado interno pode ser muito fácil cometer erros porque o jeito de captura de variável em uma closure não é entendido por todo mundo, e mesmo quem entende precisa tomar muito mais cuidado. Para problemas simples só a performance do declarativo sempre bem pior é o problema. Se precisa ou não dela é outra questão.
Para problemas simples a desvantagem do modo imperativo é só que você tem que fazer o controle na mão, mas nem fica tão longo qunto pode parecer, se fizer da forma mais idiomática:
const numeros = [1, 2, 3, 4, 5];
const quadrados = [];
for (let i = 0; i < numeros.length; i++) quadrados.push(numeros[i] * numeros[i]);
console.log(quadrados); // Output: [1, 4, 9, 16, 25]
Dá para fazer em menos linhas, mas não acho que fica legal e não deve ser a prioridade. Em caracteres dá quase o mesmo. De qualquer forma mais curto não é o mesmo que mais legível.
Legibilidade é algo contextual, para algumas pessos um pode ser mais legível que o outro, não é algo universal. Em casos mais complexos é difícil alguém achar o funcional mais legível, mas tem alguns.
Farei algo que muitos pedem para aprender a programar corretamente, gratuitamente. Para saber quando, me segue nas suas plataformas preferidas. Quase não as uso, não terá infindas notificações (links aqui).