Avoid defining a function in a loop when using an IIFE - javascript

I have the following following code:
for (i = 0; i < 5; i++) {
this.hands[0].cards[i].img.on('mousedown', (function (i) {
var j = i;
return function (event) {
self.hands[0].cards[j].holdCard();
};
})(i));
}
This is working fine for my needs but JSHint is complaining:
[L1164:C10] W083: Don't make functions within a loop.
How can I keep JSHint happy by rewriting this differently?

You can replace the IIFE with a separate function outside the loop:
function createHandler(j, self) {
return function (event) {
self.hands[0].cards[j].holdCard();
};
}
for (i = 0; i < 5; i++) {
this.hands[0].cards[i].img.on('mousedown', createHandler(i, this));
}
Useful reference: JSLint Errors Explanation (thanks to user1671639 for the link).

Related

Why Always return the last array value after an event click on marker leaflet map

I have a for loop
for (var i = 0; i < markers.length; i++) {
markers[i]["index"] = i;
}
After click to marker , Always return last item
marker.bindPopup(popup).on('popupopen',function(e){
console.log(marker["index"]);
//return last item
})
Here is an example of what is the underlying problem. It is a problem of "closures". It is a complicated topic that a lot of JS developers don't understand.
function withLet() {
for (let i = 0; i < 3; i++) {
const expectedI = i;
setTimeout(() => {
console.log('withLet', { expectedI, actualI: i });
});
}
}
function withVar() {
for (var i = 0; i < 3; i++) {
const expectedI = i;
setTimeout(() => {
console.log('withVar', { expectedI, actualI: i });
});
}
}
withLet();
withVar();
About why...
The var keywork creates the variable in the scope of the function withVar. If any function inside of the withVar function needs to access the i variable it will get the reference shared between all of the iterations of the loop.
All functions, be it event handlers or simple setTimeout will thus get the reference of the variable after all synchronous code has run.
The let keyword created a new closure, one closure per loop iteration, thus if any function inside the for loop needs to access i, it will get the reference of its own and only i.
for (let i = 0; i < 3; i++) {
const expectedI = i;
setTimeout(() => {
console.log('withLet', { expectedI, actualI: i });
});
}
console.log(i); // Error! Variable "i" is not defined!

Programmatically setting third statement of for loop

Wondering if there is by any chance to programmatically setting third statement of forloop
var conditionProgrammatically = 'i++';//or 'x--'
for (var i = 0; i < 10; conditionProgrammatically) {
console.log(i)
}
You can use any expression you want there including calling a function. You just need to be careful of scope. So, for example, this works:
var conditionProgramatically = () => i++ ;
for (var i = 0; i < 10; conditionProgramatically()) {
console.log(i)
}
But it depends on the fact that var i is in a scope shared by the function. This, however, doesn't work:
var conditionProgramatically = () => i++ ;
for (let i = 0; i < 10; conditionProgramatically()) {
console.log(i)
}
Because let is scoped to the block and not available.
Of course you can share an object which is mutable by passing it as an argument like:
fn = (o) => o.i += 1
for (let o = {i:0}; o.i < 10; fn(o)) {
console.log(o.i)
}
This allows you to use let, but is a little hard on the eyes.
All said, it's probably going to be easier to make your logic fit in a simple expression rather than calling a function. You can still perform some logic, though:
for (let i = 0; Math.abs(i) < 10; i = Math.random() > .65 ? i -1: i + 1) {
console.log(i)
}
You can set a variable and then operate with this variable according to your needs.
(remember that i-- is equivalent to i -= 1).
BTW, be careful because you would also have to change the condition, if not you will end up in an infinite loop. In your case, I would use abs()
var step = 1; // or var step = -1;
for (var i = 0; abs(i) < 10; i += step) {
console.log(i)
}
Usually, in functional programmings (like python and javascript), we can use dictionary (or objects) to store functions.
var myFunctions = {
"a": function (i) { return i + 1 },
"b": function (i) { return i - 3 }
};
Then, we can set the condition as the key to the dictionary:
myCondition = "a"; // this will set condition to increment by 1
Here is your for loop:
for (i = 0; i < n; i = myFunctions[myCondition](i)) {
// whatever
}

Take the value of a function

I have this function:
x = function() {
for (i = 0; i < window.document.querySelectorAll('.btn[href*="/p"]').length; i++) {
return window.document.querySelectorAll('.abtk[href*="/url"]')[i].href;
}
};
I would like to have in x the results of the function and now the function in self when I test it in console in Chrome. How can I do it?
Make it Immediate invoke function, so the function gets executes immediately after defining. Below is the syntax for that
x = (function() {
for (i = 0; i < window.document.querySelectorAll('.btn[href*="/p"]').length; i++) {
return window.document.querySelectorAll('.abtk[href*="/url"]')[i].href;
}
})();
Observe () at the end of function declaration.

Javascript - Variable updating

This is probably an easy question but it's late at night and I can't get my head round this.
here's my code
$(document).ready(function () {
var items = getNumber();
for (var i = 0; i < items.length; i++) {
var test = items[i].action;
test();
}
});
function getNumber()
{
var items = [];
for (var i = 0; i < 5; i++) {
var num = i * 10;
items.push({ id: i, number: num, action: function () { alert(i) } });
}
return items
}
Could someone explain to me why the alert output is always 5? I want the alert parameter to be the index at the time it is added to the array. It seems like it is being dynamic.
If you could also post a solution how i could get this to work i would be extremely thankful
This is a common issue with JavaScript variable scoping: new variables are only introduced in a new execution context and thus, in the problematic code, i is shared across all the action callbacks.
Anyway, here is the corrected code following the standard idiom:
function getNumber()
{
var items = [];
for (var i = 0; i < 5; i++) {
var num = i * 10;
items.push({
id: i, number: num,
// _i is a new variable for each callback
action: (function (_i) {
// use separate (one per callback) _i variable
return function () { alert(_i) }
})(i) // pass in current value for loop
});
}
return items
}
Alternatively, if one doesn't like all the nesting, it's fine to use a "named" function to perform the same task. The key to point is that the closure is created (and returned from) a new execution context so that a different variable is closed-over:
function getNumber()
{
function mkAction (i) {
return function () { alert(i) }
}
var items = [];
for (var i = 0; i < 5; i++) {
var num = i * 10;
items.push({
id: i, number: num,
action: mkAction(i)
});
}
return items
}
Another approach is to use Function.bind from ES5:
function getNumber()
{
var items = [];
for (var i = 0; i < 5; i++) {
var num = i * 10;
items.push({
id: i, number: num,
action: (function (_i) { alert(_i) }).bind(null, i)
});
}
return items
}
(Note that Function.bind can be emulated using a new execution context/closure even without native browser support. See the MDC documentation.)
See also:
JavaScript closure inside loops – simple practical example
Passing functions to setTimeout in a loop: always the last value?
How do JavaScript closures work?

How can I prevent this infinite loop, if I don’t know content of function

How can I isolate my variable from variable in this function, if his creator forgot for var keyword?
for (var i = 0; i < 4; i++)
{
test();
}
function test()
{
i = 0;
}
Same idea than previous answer using scoping but a better way would be to use IIFE:
(function () {
for (var i = 0; i < 4; i++) {
test();
}
})();
http://jsfiddle.net/8vBc5/
put your for loop in a separated scope:
in a function.
function test(){
i = 0;
}
function trial(){
for (var i = 0; i < 4; i++){
test();
}
}
trial();
That way only the code and functions inside the trial function can access variables declared at that level.

Categories

Resources