Q:

Error separando en palabras desde índices de caracteres no alfabéticos

Hice este código para intentar buscar palabras. Lo explico a detalle en el mismo código.
Me gustaría que me dijeran donde está mi error al pensar que me funcionaría o que está mal y cual sería la forma más óptima (pero principalmente dónde está mí error en el algoritmo, para no cometerlo en un futuro).

function principal(palabra){
 var stringArray = palabra.split(''); // Para trabajarlo como array
 var soloLetras = /[a-zA-Z]/i; // Las palabras solo se componen de letras
 var palabrasSeparadas = []; // Para guardar las palabras cuando se haya recorrido el array mediante el for que esta abajo
 var indicesNoLetras = []; // Aquí se guardan los índices de los caracteres que NO SON LETRAS, para frenar en el for.
 for(let i=0;i<stringArray.length;i++){ // Recorro la palabra
   if(soloLetras.test(stringArray[i]) === false) { // SI NO ES LETRA
     var reversa = i; // Separare las palabras con esta lógica: Cuando se encuentre un caracter que no es una letra, todas las letras anteriores a esta se agregan a un array como una palabra encontrada.
     
     indicesNoLetras.push(i); // Guardo el indice de los caracteres que NO SON LETRAS, para qué? Porque cuando la palabra por EJEMPLO, ya COMIENZE en el indice 4 del array 'stringArray' que es la que pasé por parametro, NO RETROCEDA HASTA 0, porque osino volveria a agregar todo el array, entonces retrocederá hasta UN INDICE MÁS(i+1) del último caracter QUE NO ERA UNA LETRA.
     
     var contador = 0; // Para saber en que indice de NO LETRAS, voy llevando.
     
     for(let j = reversa ; j >= indicesNoLetras[contador]; j--) { // Retrocedo desde el caracter que no es letra hasta 0, y la agrego al array.
        palabrasSeparadas.push(stringArray[j]);
        contador++; // Para que le sumo? para que si encuentra otra palabra, retroceda hasta el ultimo caracter no alfabetico encontrado y no hasta el primero, osino como ya dije agregaria practicamente todo el array.
     }
   }
 }
 return palabrasSeparadas;
}

console.log(principal('Hola ! Juan Daniel:)'));

A:

El error está en el segundo bucle for. Comento en el código.
     var contador = 0; // Se está inicializado en creo, pero indicesNoLetras puede contener índices que separaban a palabras previas

     for(let j = reversa ; j >= indicesNoLetras[contador]; j--) {
        // acá se están agregando letras al array, 
        // pero lo único que hace falta es buscar el índice 
        // y agregar la palabra completa
        contador++;
        // Es un error incrementar el contador, ya que cambiaría 
        // el índice de no letras, mientras se está decrementando a j.
     }

Siguiendo tu misma lógica, si vamos guardando los índices de caracteres no alfabéticos, sólo es necesario ver cuándo el índice anterior está distanciado en más un carácter, y ahí agregar la palabra.
No es necesario separar en letras en un array, ya que se puede acceder a cualquier caracter de un String por su índice como palabra[i].
Y para obtener una parte de un string a partir de 2 índices, usamos el método String.substring().

function principal(palabra){
    var soloLetras = /[a-záéíóúüñ]/i;
    var palabrasSeparadas = [];
    var ultimoIndiceNoLetra = -1; // Para guardar la posición del último caracter no alfabético
 
    for(let i=0;i<=palabra.length;i++){
        let finalString = (i === palabra.length);
        if(finalString || !soloLetras.test(palabra[i])) { // SI NO ES LETRA o si es el final del string
            if (i > ultimoIndiceNoLetra + 1) {
                // Si hay más de un carácter alfabético en el medio,
                // agregar la palabra
                let nuevaPalabra = palabra.substring(ultimoIndiceNoLetra + 1, i);
                palabrasSeparadas.push(nuevaPalabra);
            }
            ultimoIndiceNoLetra = i; // guardamos como último índice
        }
    }
    return palabrasSeparadas;
}

console.log(principal('Hola ! Juan Daniel:)'));

Una forma más sencilla. Y probablemente te arranques algunos pelos cuando lo veas.
La expresión regular:
/[a-záéíóúüñ]+/ig;

coincide con cualquier grupo de caracteres alfabéticos. El + del final permite que coincida con 1 o más caracteres consecutivos y el modificador /g es para que encuentre todas las coincidencias. Podemos utilizar ese regex en la función String.match():

function principal(palabra){
    return palabra.match(/[a-záéíóúüñ]+/ig);
}

console.log(principal('Hola ! Juan Daniel:)'));

