J'utilise Typeahead par Twitter. Je rencontre cet avertissement d'Intellij. Cela provoque le "window.location.href" pour chaque lien pour être le dernier élément de ma liste d'éléments.
Comment puis-je réparer mon code?
Voici mon 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);
});
}
}
};
Avec l'aide de @ cdhowie, un peu plus de code: je mettrai à jour le programme de mise à jour et aussi le href pour le clic ()
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))};
Vous devez imbriquer deux fonctions ici, créant une nouvelle fermeture qui capture la valeur de la variable (au lieu de la variable elle-même) au moment où la fermeture est créée. Vous pouvez le faire en utilisant des arguments pour une fonction externe immédiatement invoquée. Remplacez cette expression:
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;
}
Avec ça:
(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))
Notez que nous passons target
dans la fonction externe, qui devient l'argument inner_target
, capturant efficacement la valeur de target
au moment où la fonction externe est appelée. La fonction externe renvoie une fonction interne, qui utilise inner_target
au lieu de target
et inner_target
ne changera pas.
(Notez que vous pouvez renommer inner_target
à target
et tout ira bien - le target
le plus proche sera utilisé, qui serait le paramètre de la fonction. Cependant, avoir deux variables avec le même nom dans une portée aussi étroite pourrait être très déroutant et je les ai donc nommées différemment dans mon exemple afin que vous puissiez voir ce qui se passe.)
J'ai aimé le paragraphe Fermetures à l'intérieur des boucles de Javascript Garden
Il explique trois façons de le faire.
La mauvaise façon d'utiliser une fermeture à l'intérieur d'une boucle
for(var i = 0; i < 10; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
Solution 1 avec wrapper anonyme
for(var i = 0; i < 10; i++) {
(function(e) {
setTimeout(function() {
console.log(e);
}, 1000);
})(i);
}
Solution 2 - renvoyer une fonction à partir d'une fermeture
for(var i = 0; i < 10; i++) {
setTimeout((function(e) {
return function() {
console.log(e);
}
})(i), 1000)
}
Solution 3 , mon préféré, où je pense avoir enfin compris bind
- yaay! lier FTW!
for(var i = 0; i < 10; i++) {
setTimeout(console.log.bind(console, i), 1000);
}
Je recommande fortement Javascript garden - cela m'a montré cela et bien d'autres bizarreries Javascript (et m'a fait aimer JS encore plus).
p.s. si votre cerveau n'a pas fondu, vous n'avez pas eu assez de Javascript ce jour-là.
Dans ecmascript 6, nous avons de nouvelles opportunités.
L'instruction let déclare une variable locale de portée de bloc, en l'initialisant éventuellement à une valeur. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let
Étant donné que la seule portée de JavaScript est fonction portée, vous pouvez simplement déplacer la fermeture vers une fonction externe, en dehors de la portée dans laquelle vous vous trouvez.