2013-05-23 12 views
61

Sto usando Typeahead da Twitter. Mi sto imbattendo in questo avvertimento di Intellij. Ciò sta causando "window.location.href" per ciascun link come l'ultimo elemento nel mio elenco di elementi.La variabile mutabile è accessibile dalla chiusura. Come posso risolvere questo?

Come posso risolvere il mio codice?

Qui di seguito è il mio codice:

AutoSuggest.prototype.config = function() { 
    var me = this; 
    var comp, options; 
    var gotoUrl = "/{0}/{1}"; 
    var imgurl = '<img src="/icon/{0}.gif"/>'; 
    var target; 

    for (var i = 0; i < me.targets.length; i++) { 
     target = me.targets[i]; 
     if ($("#" + target.inputId).length != 0) { 
      options = { 
       source: function (query, process) { // where to get the data 
        process(me.results); 
       }, 

       // set max results to display 
       items: 10, 

       matcher: function (item) { // how to make sure the result select is correct/matching 
        // we check the query against the ticker then the company name 
        comp = me.map[item]; 
        var symbol = comp.s.toLowerCase(); 
        return (this.query.trim().toLowerCase() == symbol.substring(0, 1) || 
         comp.c.toLowerCase().indexOf(this.query.trim().toLowerCase()) != -1); 
       }, 

       highlighter: function (item) { // how to show the data 
        comp = me.map[item]; 
        if (typeof comp === 'undefined') { 
         return "<span>No Match Found.</span>"; 
        } 

        if (comp.t == 0) { 
         imgurl = comp.v; 
        } else if (comp.t == -1) { 
         imgurl = me.format(imgurl, "empty"); 
        } else { 
         imgurl = me.format(imgurl, comp.t); 
        } 

        return "\n<span id='compVenue'>" + imgurl + "</span>" + 
         "\n<span id='compSymbol'><b>" + comp.s + "</b></span>" + 
         "\n<span id='compName'>" + comp.c + "</span>"; 
       }, 

       sorter: function (items) { // sort our results 
        if (items.length == 0) { 
         items.push(Object()); 
        } 

        return items; 
       }, 
// the problem starts here when i start using target inside the functions 
       updater: function (item) { // what to do when item is selected 
        comp = me.map[item]; 
        if (typeof comp === 'undefined') { 
         return this.query; 
        } 

        window.location.href = me.format(gotoUrl, comp.s, target.destination); 

        return item; 
       } 
      }; 

      $("#" + target.inputId).typeahead(options); 

      // lastly, set up the functions for the buttons 
      $("#" + target.buttonId).click(function() { 
       window.location.href = me.format(gotoUrl, $("#" + target.inputId).val(), target.destination); 
      }); 
     } 
    } 
}; 

Con l'aiuto di @ cdhowie, ancora un po 'di codice: io aggiornare il programma di aggiornamento e anche il href per il click()

updater: (function (inner_target) { // what to do when item is selected 
    return function (item) { 
     comp = me.map[item]; 
     if (typeof comp === 'undefined') { 
      return this.query; 
     } 

     window.location.href = me.format(gotoUrl, comp.s, inner_target.destination); 
     return item; 
}}(target))}; 
+1

Yao, (commento generale non legati alla la domanda): 'matcher' non include lo stesso' if (typeof comp === 'undefined') {...} 'test di sicurezza incluso in' evidenziatore' e 'updater'. In una rapida occhiata al codice, sembra che questa potrebbe essere una svista. –

+0

@ Barbabietola-Barbabietola ooohhhhhh buona presa !!! grazie!!! sorprendentemente non ho ricevuto alcun errore quando non c'è corrispondenza =/ – iCodeLikeImDrunk

+1

Yao, sembra che tu abbia una buona tolleranza agli errori in altre parti del tuo codice, ma "belt + braces" non fa mai male a nessuno. –

risposta

56

È necessario nidificare due funzioni qui, creando una nuova chiusura che cattura il valore della variabile (invece della variabile stessa) al momento della creazione della chiusura. Puoi farlo usando gli argomenti per una funzione esterna immediatamente invocata. Sostituire questa espressione:

function (item) { // what to do when item is selected 
    comp = me.map[item]; 
    if (typeof comp === 'undefined') { 
     return this.query; 
    } 

    window.location.href = me.format(gotoUrl, comp.s, target.destination); 

    return item; 
} 

Con questa:

(function (inner_target) { 
    return function (item) { // what to do when item is selected 
     comp = me.map[item]; 
     if (typeof comp === 'undefined') { 
      return this.query; 
     } 

     window.location.href = me.format(gotoUrl, comp.s, inner_target.destination); 

     return item; 
    } 
}(target)) 

noti che passiamo target nella funzione esterno, che diventa l'argomento inner_target, catturando efficacemente il valore di target momentaneamente la funzione esterna è chiamato. La funzione esterna restituisce una funzione interna, che utilizza inner_target anziché target e inner_target non cambia.

(Si noti che è possibile rinominare inner_target a target e sarete a posto - sarà utilizzato il più vicino target, che sarebbe il parametro della funzione Tuttavia, avendo due variabili con lo stesso nome in un ambito così stretta potrebbe essere. molto confuso e così li ho chiamato in modo diverso nel mio esempio in modo che si può vedere cosa sta succedendo.)

+0

e la seconda parte? dove ho impostato l'href per il pulsante? questo modo di fare le cose mi sembra super strano =/ – iCodeLikeImDrunk

+2

@yaojiang Applica qui la stessa tecnica. Se hai bisogno di aiuto fammi sapere e possiamo passare alla chat. Ma prima cerca di capire cosa sta succedendo nel mio esempio e prova a farlo da solo. (Il concetto si attaccherà meglio se lo fai tu stesso!) – cdhowie

+0

aggiornerò con il mio codice a breve. – iCodeLikeImDrunk

124

mi è piaciuto il paragrafo Chiusure All'interno Loops da Javascript Garden

spiega tre modi di farlo .

Il modo sbagliato di utilizzare una chiusura all'interno di un ciclo

for(var i = 0; i < 10; i++) { 
    setTimeout(function() { 
     console.log(i); 
    }, 1000); 
} 

Soluzione 1 con involucro anonimo

for(var i = 0; i < 10; i++) { 
    (function(e) { 
     setTimeout(function() { 
      console.log(e); 
     }, 1000); 
    })(i); 
} 

Soluzione 2 - il ritorno di una funzione da una chiusura

for(var i = 0; i < 10; i++) { 
    setTimeout((function(e) { 
     return function() { 
      console.log(e); 
     } 
    })(i), 1000) 
} 

Soluzione 3, il mio preferito, dove penso di aver finalmente capito bind - yaay! legare FTW!

for(var i = 0; i < 10; i++) { 
    setTimeout(console.log.bind(console, i), 1000); 
} 

consiglio vivamente Javascript garden - è me questa e molte altre stranezze Javascript ha mostrato (e mi ha fatto come JS anche di più).

p.s. se il tuo cervello non si scioglie non hai avuto abbastanza Javascript quel giorno.

1

Poiché l'unica scoping che JavaScript ha è funzione campo di applicazione, è sufficiente spostare la chiusura a una funzione esterna, al di fuori del campo di applicazione si è in.

Problemi correlati