Ich benutze Typeahead von Twitter. Ich stoße auf diese Warnung von Intellij. Dies führt dazu, dass "window.location.href" für jeden Link das letzte Element in meiner Elementliste ist.
Wie kann ich meinen Code reparieren?
Unten ist mein Code:
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);
});
}
}
};
Mit @ cdhowies Hilfe noch etwas Code: Ich werde den Updater und auch die href für den click () aktualisieren.
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))};
javascript
closures
mutable
iCodeLikeImDrunk
quelle
quelle
Antworten:
Sie müssen hier zwei Funktionen verschachteln und einen neuen Abschluss erstellen, der den Wert der Variablen (anstelle der Variablen selbst) zum Zeitpunkt des Abschlusses erfasst . Sie können dies mithilfe von Argumenten für eine sofort aufgerufene äußere Funktion tun. Ersetzen Sie diesen Ausdruck:
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; }
Mit diesem:
(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))
Beachten Sie, dass wir
target
in die äußere Funktion übergehen , die zum Argumentinner_target
wird und den Wert vontarget
zum Zeitpunkt des Aufrufs der äußeren Funktion effektiv erfasst . Die äußere Funktion gibt eine innere Funktion zurück, dieinner_target
anstelle von verwendettarget
wird undinner_target
sich nicht ändert.(Beachten Sie, dass Sie umbenennen
inner_target
zutarget
und Sie werden in Ordnung sein - in der Nähetarget
. Verwendet werden, welche die Funktionsparameter wäre jedoch mit zwei Variablen mit dem gleichen Namen in einem so engen Rahmen sehr verwirrend sein könnte und so ich genannt habe sie in meinem Beispiel anders, damit Sie sehen können, was los ist.)quelle
Ich mochte den Absatz Closures Inside Loops von Javascript Garden
Es werden drei Möglichkeiten erläutert.
Die falsche Verwendung eines Verschlusses innerhalb einer Schleife
for(var i = 0; i < 10; i++) { setTimeout(function() { console.log(i); }, 1000); }
Lösung 1 mit anonymem Wrapper
for(var i = 0; i < 10; i++) { (function(e) { setTimeout(function() { console.log(e); }, 1000); })(i); }
Lösung 2 - Rückgabe einer Funktion aus einem Verschluss
for(var i = 0; i < 10; i++) { setTimeout((function(e) { return function() { console.log(e); } })(i), 1000) }
Lösung 3 , mein Favorit, wo ich glaube, ich habe es endlich verstanden
bind
- yaay! FTW binden!for(var i = 0; i < 10; i++) { setTimeout(console.log.bind(console, i), 1000); }
Ich kann den Javascript-Garten nur empfehlen - er hat mir diese und viele weitere Javascript-Macken gezeigt (und mich dazu gebracht, JS noch mehr zu mögen).
ps Wenn dein Gehirn nicht geschmolzen ist, hattest du an diesem Tag nicht genug Javascript.
quelle
In Ecmascript 6 haben wir neue Möglichkeiten.
Die let- Anweisung deklariert eine lokale Variable für den Blockbereich und initialisiert sie optional mit einem Wert. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let
quelle
Da der einzige Bereich, über den JavaScript verfügt, der Funktionsumfang ist , können Sie den Abschluss einfach auf eine externe Funktion außerhalb des Bereichs verschieben, in dem Sie sich befinden.
quelle
Um die Antwort von @BogdanRuzhitskiy zu verdeutlichen (da ich nicht herausfinden konnte, wie der Code in einen Kommentar eingefügt werden kann), besteht die Idee bei der Verwendung von let darin, eine lokale Variable innerhalb des for-Blocks zu erstellen:
for(var i = 0; i < 10; i++) { let captureI = i; setTimeout(function() { console.log(captureI); }, 1000); }
Dies funktioniert in so ziemlich jedem modernen Browser außer IE11.
quelle