Perchè fa così, cosa sto sbagliando (concettualmente)?
Ci sono diversi problemi.
Innanzitutto hai fatto un errore "grossolano" nella specificazione degli argomenti per la funzione setTimeout().
Questo modo è concettualmente sbagliato:
codice:
timer=setTimeout(caretWrite(caretRepeat,tempo,0),tempo);
Il primo argomento è un callback, cioè deve essere specificata una funzione o un riferimento ad una funzione o una stringa che specifica la chiamata ad una funzione (vedi documentazione e alcune indicazioni su questo link). Tu invece hai indicato una chiamata diretta alla tua funzione caretWrite(), come se tale funzione dovesse restituire qualcosa da passare, di conseguenza, come argomento a setTimeout. La tua funzione, infatti, viene eseguita istantaneamente, peraltro senza restituire alcunché, quindi stai semplicemente specificando il valore undefined come primo argomento di setTimeout().
Ad ogni modo, lo script si interrompe prima che sia definito questo primo argomento. Infatti, durante l'esecuzione della tua funzione, avviene l'errore segnalato in console. Questo errore avverrebbe comunque anche sistemando opportunamente la specificazione del setTimeout() perché, come tu hai supposto, c'è comunque un problema di visibilità delle variabili.
Penso sia un errore di scope che si perda la variabile timer anche se gerarchicamente è nella funzione "superiore"
Questo non è proprio esatto. Sicuramente è un problema di scope ma di fatto la funzione caretWrite() non sta dentro scrivi(). Tu hai semplicemente fatto una chiamata tra una funzione e l'altra, ma queste restano comunque slegate. I loro scope sono ben distinti.
Ultima considerazione: anche riuscendo a sistemare questi errori, personalmente ti consiglierei l'uso del setInterval() (più opportuno in questo caso) e di una funzione anonima per il callback (o comunque una funzione dichiarata dentro quella principale).
Un semplice esempio, chiaramente andrà completato (nel tuo script non viene scritto alcun testo):
codice:
function scrivi(){
var testo="Hi",
id='saluto',
tempo=600,
ripeti=6,
intervallo = setInterval(function(){
if (!--ripeti) clearInterval(intervallo);
$("#"+id).html(ripeti%2?"|":"");
},tempo);
}