Visualizzazione dei risultati da 1 a 5 su 5
  1. #1

    Scope variabili sottofunzioni

    Voglio emulare il cursore di scrittura che lampeggia per un po' e poi scrive del testo, come se stesse scrivendo dal vivo...
    Questo è il codice:

    codice:
    function caretWrite(repeat,tempo,step){
       console.log('1');
    step+=1;
    clearTimeout(timer);
    
    if (step < repeat) {
    
          var val=$('#saluto').html()=='' ? "|" : '';
    $('#saluto').html(val);
    
    timer=setTimeout(caretWrite(repeat,tempo,step),tempo);
    
    
    }
    
    }
    
    function scrivi(){
    
       var text="Hi";
    var id='saluto';
    var tempo=1000;
    var caretRepeat=6;
    var timer;
    
    timer=setTimeout(caretWrite(caretRepeat,tempo,0),tempo);
    
    }
    
    $(document).ready(function(){
       scrivi();
    console.log('2');
    });
    Allora la console mi da errore che non trova timer nella cleartimeout...
    Penso sia un errore di scope che si perda la variabile timer anche se gerarchicamente è nella funzione "superiore"...
    Pensavo poi che magari il problema fosse la chiamata asincrona del timer che magari quando viene richiamata la funzione carewrite è "già finita" la funziona scrivi() e quindi ha rilasciato la variabile timer, per questo ho messo i log di 1 e 2, in console ho il log di 1 (quindi subito dopo l'errore) e non di 2, quindi penso che alla chiamata del timer sia ancora "attiva" la funzione scrivi()...
    Ho un po' di confusione...

    Perchè fa così, cosa sto sbagliando (concettualmente)?

    Grazie
    Francesco

  2. #2
    Moderatore di CSS L'avatar di KillerWorm
    Registrato dal
    Apr 2004
    Messaggi
    5,771
    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);
    }
    Installa Forum HTML.it Toolset per una fruizione ottimale del Forum

  3. #3
    Grazie per la risposta, in effetti è stato un errore abbastanza stupido quello di passare così i parametri...
    Voglio usare il settimeout più che altro per "esercizio"...

    Per passarli sto scoprendo ora le "closure" e devo dire che stento a capirle...
    Soprattutto mi dico: se con la closure tengo "attivo" lo scope della funziona padre anche quando cessa mantenendo le variabili in memoria, tanto vale che le faccia direttamente globali no?

    Ho anche visto molti esempi dove si usa la sintassi (function(param){})(param) di cui non ho trovato molto...
    Come funziona? Fa un riferimento implicito alla funzione .call(funzione anomima, arg)?
    E' giusto che trovi questo esempio tra quelli delle closure?

    Questo è un esempio che ho trovato:
    codice:
    el.addEventListener('click', (function(str) {    return function() {
          alert(str);
        }
      })("Hello"), false);
    Questo è un altro esempio:
    codice:
      var contents=["uno","due","tre","quattro"];
      var els=document.getElementsByTagName('P');
      for (var i=0; i < els.length; i++) {
        els[i].onclick=(function(index) {
          return function() {
            this.innerHTML=contents[index];
          }
        })(i);
      }
    Di questo non capisco come funziona il "index" nella funzione che viene restituita runtime...
    Sostituisce la variabile index con il suo valore quasi come fosse una costante?
    Dopo avere eseguito quel for avrei quindi nella callback del click una funzione così:
    [CODE]
    function() {
    this.innerHTML=contents[1]; <- 2 / 3 / 4, valori già settati
    }
    [/CODE]
    Oppure al click del mouse risale allo scope dell'index della funzione?
    In questo caso allora dovrebbe risalire all'ultimo valore della variabile index, quindi ad ogni P dovrebbe associare l'ultimo indice no?

    Grazie

    PS. del tuo codice non ho capito una cosa, se eseguirò la funzione intervallo dopo un tot di ms, ammettendo che la funziona "padre" sia già terminata, senza un closure o qualche altro stratagemma coma faccio a risalire al valore di ripeti (o è dichiarato senza var per essere volutamente globale)?
    Ultima modifica di Francesco95; 02-09-2015 a 00:09
    Francesco

  4. #4
    Moderatore di CSS L'avatar di KillerWorm
    Registrato dal
    Apr 2004
    Messaggi
    5,771
    Voglio usare il settimeout più che altro per "esercizio"...
    Ok.

    Per passarli sto scoprendo ora le "closure" e devo dire che stento a capirle...
    Soprattutto mi dico: se con la closure tengo "attivo" lo scope della funziona padre anche quando cessa mantenendo le variabili in memoria, tanto vale che le faccia direttamente globali no?
    Dipende tutto da ciò che vuoi ottenere. Forse nel tuo caso specifico la situazione cambierebbe poco se tu usassi variabili locali oppure globali, ma è chiaro che in un contesto più ampio può risultare sconveniente usare delle variabili globali che non dovrebbero essere visibili se non solamente all'interno di quella specifica funzione. Tali variabili potrebbero rivelarsi delle mine vaganti. La cosa più banale che potrebbe succedere è un conflitto tra nomi-variabile già utilizzati, con conseguenti risultati imprevedibili. Meglio quindi che ogni variabile sia dichiarata, per quello che serve, nel proprio ambito.
    La closure non è altro che una funzione nidificata che è comunque resa accessibile dall'esterno della funzione in cui è definita.
    Come tu scrivi, le closure conservano l'ambiente della funzione padre. In sostanza, da una closure è possibile accedere alle variabili dichiarate nella funzione padre. L'aspetto importante è che, anche quando la funzione padre è terminata, tali variabili, se usate all'interno della closure, restano in vita fintanto che la closure stessa continua ad esistere.

    Vedi:
    https://developer.mozilla.org/it/doc...cript/Closures

    Ho anche visto molti esempi dove si usa la sintassi (function(param){})(param) di cui non ho trovato molto...
    Come funziona?
    Quella sintassi (immagino che tu abbia già cercato qualcosa) identifica semplicemente una funzione anonima auto-eseguibile (o IIFE).
    In sintesi, serve per definire particolari ambiti, generalmente "momentanei", per cui la funzione anonima si esegue automaticamente e poi muore lì una volta terminata.
    Questa tecnica è usata in vari contesti. Nel caso più semplice puoi usarla se vuoi eseguire un blocco di script, con tutte le sue variabili locali, ma non ti serve poi mantenerlo in memoria come, invece, nel caso di una normale funzione.
    Spesso viene usata per creare degli scope in cui passare degli alias di variabili in modo da non avere conflitti con gli stessi nomi-variabile che sono presumibilmente già utilizzati, a livello globale, per altre applicazioni. Questo è il caso, ad esempio, dei conflitti tra framework o librerie in cui lo stesso nome-variabile è usato per l'oggetto (o funzione) di base che li costituisce... ma questa è un'altra storia.

    Fa un riferimento implicito alla funzione .call(funzione anomima, arg)?
    La sintassi credo sia scorretta; call() è un metodo dell'"oggetto function" e il primo argomento specifica il contesto da considerare per la funzione chiamata.
    Non capisco cosa intendi per "riferimento implicito". Per alcuni versi potresti ottenere risultati analoghi tra una IIFE e l'uso di questo metodo ma, ad ogni modo, ci sono delle differenze sostanziali.

    E' giusto che trovi questo esempio tra quelli delle closure?
    Relativamente alle closure, si utilizza la tecnica delle IIFE in cui, in linea di massima, la IIFE restituisce a sua volta una funzione per permettere così la gestione di variabili private.

    Vedi:
    http://www.w3schools.com/js/js_function_closures.asp (l'esempio a fondo pagina)

    Questo è un esempio che ho trovato:
    codice:
    el.addEventListener('click', (function(str) {    return function() {
          alert(str);
        }
      })("Hello"), false);
    Sì, in questo caso credo che il listener, dell'evento click, sia una closure dal momento che va a richiamare una funzione interna (cioè definita dentro quella principale) che usa la variabile definita, anch'essa, nello scope della funzione padre.
    La funzione principale, in questo esempio, è una IIFE ma questo non deve lasciarti pensare che la closure non possa essere creata anche senza l'uso di quella tecnica. Infatti puoi ottenere una cosa analoga usando una normale funzione.
    codice:
    function normaleFunzione(str) {
      return function() {
        alert(str);
      }
    }
    el.addEventListener('click', normaleFunzione("Hello"), false);
    In questo modo è chiaro però che la normaleFunzione resta dichiarata ed accessibile anche se non ci serve (ecco perché la IIFE risulta essere più opportuna in questo caso).

    Questo è un altro esempio:
    codice:
      var contents=["uno","due","tre","quattro"];
      var els=document.getElementsByTagName('P');
      for (var i=0; i < els.length; i++) {
        els[i].onclick=(function(index) {
          return function() {
            this.innerHTML=contents[index];
          }
        })(i);
      }
    Di questo non capisco come funziona il "index" nella funzione che viene restituita runtime...
    Per ogni [I]els ciclato, viene definito l'onclick che di fatto è una closure.
    Come tu ti saresti aspettato, la variabile index avrebbe dovuto assumere l'ultimo valore uscente dal ciclo, definendo questo come valore passato ai vari onclick applicati ai relativi elementi. Questo infatti è un comune problema quando si vuole applicare un valore, prodotto da un ciclo, ad un evento (tipo click). Ecco perché si usano le closure.
    Con la closure, la relativa funzione interna mantiene infatti lo stato della variabile index che di fatto è dichiarata e definita nella funzione padre. Ma qual'è questo valore? Non è altro che il valore (i) passato alla funzione stessa come parametro.
    La variabile i alla fine del ciclo assumerà l'ultimo valore, ma le diverse variabili (locali) index, mantenute in vita delle relative closure, andranno a mantenere anche il loro valore precedentemente definito.

    Non saprei spiegartelo meglio di così.

    PS. del tuo codice non ho capito una cosa, se eseguirò la funzione intervallo dopo un tot di ms, ammettendo che la funziona "padre" sia già terminata, senza un closure o qualche altro stratagemma coma faccio a risalire al valore di ripeti
    Credo che anche qui si possa parlare di closure.
    Per il setInterval ho definito una funzione anonima, che di fatto risiede nella funzione padre scrivi. Tale funzione anonima viene quindi richiamata come callback del setInterval e, per il principio detto inizialmente, viene mantenuto l'ambiente della funzione padre. Quindi tutte le variabili utilizzate sono mantenute in vita con i loro specifici valori.

    (o è dichiarato senza var per essere volutamente globale)?
    No, non si tratta di una variabile globale. Fai attenzione, tutte le variabili che ho definito (comprese ripeti ed intervallo), dentro la funzione scrivi, sono locali perché dichiarate proprio coll'istruzione var. Ho semplicemente usato una forma contratta separando, con la virgola, le varie dichiarazioni.
    Installa Forum HTML.it Toolset per una fruizione ottimale del Forum

  5. #5
    Grazie mille, ho capito
    Francesco

Permessi di invio

  • Non puoi inserire discussioni
  • Non puoi inserire repliche
  • Non puoi inserire allegati
  • Non puoi modificare i tuoi messaggi
  •  
Powered by vBulletin® Version 4.2.1
Copyright © 2025 vBulletin Solutions, Inc. All rights reserved.