functions within loops, why are they considered an 'error' - javascript

For complex functions declared within a loop, I can see why I wouldn't want to do this, but why would it be be considered bad javascript?
We can name the function and place it outside the loop of course, but upsets the flow for something that is simple ( no async ).
Eg, below is a simple inline function declaration within a loop ( JSHINT/LINT complains, why this is considered a no no ?
for (var i = 0, len=arr.length; i < len; ++i) {
dosomething(arr[i], function(returnvalue) {
console.log(returnvalue);
});
};

Here's one reason why you wouldn't want that. The function references the same vars.
http://jsfiddle.net/RCzyF/
var a = [];
for(var i=0; i<10; i++) {
a.push(function () {
return i;
});
}
h = "";
for(var j=0; j<10; j++) {
h += "" + a[j]();
}
alert(h);
One could expect to see 0123456789 but it will append 10 10 times to h instead. It can make code really hard to understand when one function might change the content of other functions.
Here's a more complex example how things can get wrong.
var a = [];
for(var i=0; i<10; i++) {
a.push(function () {
return i++;
});
}
h = "";
for(var j=0; j<10; j++) {
h += "" + a[j]();
}
alert(h);
When the functions are created, they point to the same lexical scope. When the function are executed, they change the value inside the function and each function in the array still point to the same value. This can lead to really hard bug to debug when a variable gets modified but you didn't directly modify it.
Also here's the real answer coming from jslint itself: http://jslinterrors.com/dont-make-functions-within-a-loop/

Creating a function at each iteration is uselessly heavy.
Most of the time, in client side JavaScript, performance doesn't matter and there's no problem but it's better to take and keep good habits than having later to optimize the code (as long as the readability isn't hindered).
Here's a proof that you create a new function at each iteration :
var old;
function compare(_, a){
if (old) console.log('equal ?', old==a);
else old = a;
}
for (var i=0; i<2; i++){
compare(i, function(i) { return i*i });
}
It logs 'equal' ? false
testable jsbin

Related

javascript - JSTree - why is recursion stopped in javascript?

I try to remove a sub-tree ( at least one node ) from a tree, so I build a recursive function :
function remove(i)
{
tree = $('#jstree-tree').jstree(true).get_json('#', {flat:true});
j=0;
console.log(tree);
while(j<tree.length)
{
if(tree[i].id == tree[j].parent)
{
remove(j);
j--;
}
j++;
}
tree.splice(i,1);
}
but the problem here is the Recursion is stopped after the first call, I don't know why this my code.
Now j is a global variable because of declaration without var.
Thefore the value of j is affected other executions of remove().
Changing j=0; to var j=0; fix this problem, but it cause infinit loop.
It seems that the recursion of remove() is blocking the reflection of tree value to $('#jstree-tree').
To avoid this issue, do tree = ...get_json(...) at the out side of the recursion and reuse it.
This is working code:
function remove(arg)
{
tree = $('#jstree-tree').jstree(true).get_json('#', {flat:true});
var _remove = function (i) {
var j = 0;
while(j < tree.length)
{
if(tree[i].id == tree[j].parent)
{
_remove(j);
j--;
}
j++;
}
tree.splice(i,1);
}
_remove(arg);
}

Javascript function with dynamically generated arguments

Below code :
loop(n times)
create HTML Button Element
count++;
assign onclick event = function(){
openSomething("Value_"+count)
}
so if i create 3 input elements (n=3) and then go back click any of the three buttons then every time openSomething("Value_"+3) only gets called.
why openSomething("Value_"+1) and openSomething("Value_"+2) does not get called?
I am not sure what is going on may be it the scope issue but i dont know much about scope either, any help to push me in the right direction is much appreciated.
My original code
var count = 0;
for(var i =0;i<someValue;i++){
count++;
var button = document.createElement("img");
button.src = "/images/small_button.gif";
button.imageButton = true;
button.srcBase = "/images/small_button";
button.onclick = function () {
selectSomething("someIdText_"+count);};
cell.appendChild(button);
}
Because JavaScript doesn't have block-level scoping of variables, and as a result everything is scoped to the function. That means that when you have code that uses a variable (like your loop counter n or your count variable) at a later point (i.e. after the full execution of the function), it will have its value set to the last value for the loop. You need to create a closure (a new scope for the variable) inside of your loop. Something like this (since you didn't post your actual code):
for(var i = 0, l = list.length; i < l; i++) {
(function(count) {
something.onclick = function() {
openSomething("Value_" + count);
}
})(i);
}
For a more modern approtce use let,
works for firefox, chrome, and node
if you need to target all the browsers, use Anthony approach
for(var count = 0, l = list.length; count < l; count++) {
let count;
something.onclick = function() {
openSomething("Value_" + count);
}
}

Using javascript closures for context in loops

Is it bad practice to make a jQuery closure simply for the purpose of retaining context?
Example:
{
testFunction: function() {
//Instantiate variables etc...
for (var i = 0; i < columns.length; i++) {
for (var j = 0; j < columns[i].length; j++) {
// create a closure so the .then callback has the correct value for x
var func = function() {
var x = new testObject(columns[i][j].Id);
// find method returns a jQuery promise
x.find().then(function() {
// use x for some logic here
});
};
func();
}
}
}
}
In this case the closure is needed so that the function for the jQuery promise has context for what x is. Otherwise the x variable is changed after the next iteration of the loop. The jQuery promise object gets the value of whatever x was in the last iteration of the loop.
I'm looking for best practices here, just wondering what to do to keep code simple, readable and efficient.
Please note any performance issues that come along with closures/not using closures. References are appreciated.
Using a closure is definitely a good practice, but you can simplify it a little:
Pass in the object directly to your closure.
Use an IIFE instead of a named function declaration.
for (var i = 0; i < columns.length; i++) {
for (var j = 0; j < columns[i].length; j++) {
(function(x) {
x.find().then(function() {
// use x for some logic here
});
}( new testObject(columns[i][j].Id) ));
}
}
Note: you could simplify this code a little by using jQuery's $.each() method, but using a regular for loop is way faster.
jQuery (or ES5) iterators usually lead to cleaner code compared to loops. Consider:
$.each(columns, function() {
$.each(this, function() {
var x = new testObject(this.Id);
x.find().then(function() {
// stuff
});
});
});
Note that the scoping problem doesn't arise here, since you get a new scope on every iteration automatically. To address the performance question, iterators are slower than loops, but in the event-driven world you hardly need to worry about that.
Unfortunately there is no a better way to solve the problem. However, you miss to pass i and j:
for (var i = 0; i < columns.length; i++) {
for (var j = 0; j < columns[i].length; j++) {
// create a closure so the .then callback has the correct value for x
var func = function(i, j) {
var x = new testObject(columns[i][j].Id);
// find method returns a jQuery promise
x.find().then(function() {
// use x for some logic here
});
};
func(i, j);
}
}
It is ok and it's actually the only way to create a context.
Unfortunately for some reasons I don't really understand there are Javascript engines that limit the number of nested levels of functions you can create to very low numbers so just try to use them only when really needed (e.g. I found that my quite powerful Galaxy S4 with Android only can handle 18 nested levels).
For hand-written code this is not an issue normally, but for generated javascript code it's quite easy to get past those untold limits.

JavaScript addEventListener() not working as expected

I have never used addEventListener(), but I cannot write the HTML equivalent I would like for each <div> I am treating as a button because of the way I am generating content. The equivalent would be:
<div onmousedown="jsItems[someId].toggleImage(someGallery, someIndex);"></div>
What I've been trying is this:
JsTree.prototype.addGalleries = function(inElements) {
// ...unrelated code here removed for StackOverflow...
for (var i = 0; i < this.jsGalleries.length; i++) {
for (var j = 0; j < this.jsGalleries[i].buttons.length; j++) {
var self = this;
this.jsGalleries[i].buttons[j].addEventListener("mousedown", function() {
self.toggleImage(i, j);
});
}
}
}
Where i counts from 0 to 1 and j counts from 0 to 2 (for both i in this case), i represents someGallery, j represents someIndex, and I could access someId with this.id inside the code above (or with self.id inside addEventListener's function).
The problem is that although clicking on one of these "buttons" (<div>s) does trigger:
JsTree.prototype.toggleImage = function(inGallery, inIndex) {
alert(this.id+", "+inGallery+", "+inIndex);
}
that it always alerts "8, 2, 3" regardless of which button is clicked. The "8" is correct but I have no idea why "2" or "3" are alerted. They seem to just be 1 more than what i and j count to (verified by trying j < this.jsGalleries[i].buttons.length-1 which alerts "8, 2, 2").
Edit: someId, someGallery, and someIndex are not real variables, they are junk I made up to try to explain the problem.
This is a classic JS mistake. The problem is that the values of i and j are not captured in any function scope, and your event handlers are asynchronous. That means that when your event handler runs, both for loops have run to completion, thus i == this.jsGalleries.length and j === this.jsGalleries[this.jsGalleries.length - 1].buttons.length.
Try out one of these:
JsTree.prototype.addGalleries = function(inElements) {
// ...unrelated code here removed for StackOverflow...
for (var i = 0; i < this.jsGalleries.length; i++) {
for (var j = 0; j < this.jsGalleries[i].buttons.length; j++) {
(function(self, innerI, innerJ){
var galleryEl = self.jsGalleries[innerI].buttons[innerJ];
galleryEl.addEventListener("mousedown", function() {
self.toggleImage(innerI, innerJ);
});
})(this, i, j);
}
}
}
Or possibly clearer:
JsTree.prototype.addGalleries = function(inElements) {
// ...unrelated code here removed for StackOverflow...
var addHandler = function(self, i, j){
self.jsGalleries[i].buttons[j].addEventListener("mousedown", function() {
self.toggleImage(i, j);
});
};
for (var i = 0; i < this.jsGalleries.length; i++) {
for (var j = 0; j < this.jsGalleries[i].buttons.length; j++) {
addHandler(this, i, j);
}
}
}
It's not a problem with addEventListener. This is a common mistake. In order to understand what's going on, I have to explain how closures work.
When you have a loop and a function inside of it:
var i = 5;
while(i--){
setTimeout(function(){
console.log(i);
}, 100);
}
Each function is given a reference to the variable i. That means that they don't retain the value of i at the time you defined them. Again, I'll restate, each function has a reference to the same variable i, not to the value that it had at the time the function was declared. In my example above, all of the setTimeout's are defined asynchronously. The anonymous functions all fire at 100 milliseconds and each one logs the value that's in i at the time that the function was run. In my example, that value would be -1 for all the functions.
There are 2 ways to solve this. I'll show you the easy one first:
for (var i = 0; i < this.jsGalleries.length; i++) {
for (var j = 0; j < this.jsGalleries[i].buttons.length; j++) {
var self = this;
self.gallery = {i: i, j: j};
this.jsGalleries[i].buttons[j].addEventListener("mousedown", function() {
self.toggleImage(self.gallery.i, self.gallery.j);
});
}
}
Here, you're storing the values on the actual DOM element. These values are equivalent to the values at the time that the loop was run, so the event listener grabs the correct value. Notice I nested the value in an object called gallery. I did this to kind of namespace it. It's not a good idea to store values on elements in the DOM, just in case browsers end up implementing a property with the same name. I feel like gallery is safe enough.
The other option, and probably the best practice, for fixing this is to use closures to your advantage.
for (var i = 0; i < this.jsGalleries.length; i++) {
for (var j = 0; j < this.jsGalleries[i].buttons.length; j++) {
var self = this;
this.jsGalleries[i].buttons[j].addEventListener("mousedown", (function closure(self, i, j){
return function actualListener(){
self.toggleImage(i, j);
}
})(self, i, j));
}
}
In this case, we create a self executing function (called closure in my example) which runs immediately when we're creating the listener. Let me state it again, this function runs the moment the listener is being added, NOT when it's run. The reason we do this is so we can pass in the values we want to save for later, in this case, self, i, and j. Then, when the event occurs, the function that ACTUALLY gets run is the inner function (called actualListener). actualListener has a copy of all the values stored in its closure at the time that the closure function was run.

Variable definitions in nested for loops?

Some static languages like Java seem to have very special rules for variables defined in the first argument of a for loop. They are accessible only by the given loop, which makes them behave pretty much like javascript functions' local variables and arguments. I mean stuff like this:
class ForVariable {
public static void main(String[] args) {
for(int i = 0; i != 0; i++) {}
System.out.println(i); // Throws an Exception
}
}
Javascript doesn't behave like that, which makes nesting loops quite a messy business. My question is: Is it valid to declare variables in the subsequent loops via the var keyword? In other words - which of the following examples is valid?
for(var i = 0, j; i < 5; i++) {
for(j = 0; j < 10; j++) <do some stuff>;
}
OR
for(var i = 0; i < 5; i++) {
for(var j = 0; j < 10; j++) <do some stuff>;
}
Clearly it is wrong to declare a variable several times, which would make the 2nd example a no-go, but given the fact that the 1st example is the way loops nesting is done in most languages I know, I'm rather hesitant to declare the winner.
Those are both valid. Function scoped vs block scoped. Basically both loops in JavaScript become:
function a () {
var i, j;
for(i = 0, j; i < 5; i++) {
for(j = 0; j < 10; j++) <do some stuff>;
}
}
because the var declarations are hoisted to the top
Its not wrong to declare a variable several times. For instance there is really no problem with:
var i = 0;
var i = 1;
That's valid JavaScript. Good tools like the Closure Compiler will generate a warning though because you typically don't intend to do that.
That being said, even the Closure Compiler won't generate a warning for your example #2. It's just common convention in JS even if you are technically re-declaring.
Either of your two examples is fine but the second one is a little more sensible to parse. I wouldn't worry about it either way.
You don't want to be using the var keyword, but rather function arguments, because javascript is not block-scoped. For example:
[100,200,300].forEach(function (x,i) {
[10,20,30].forEach(function (y,j) {
console.log('loop variables, indices '+[i,j]+' have values: '+[x,y]);
});
})
or
[100,200,300].map(function (x,i) {
return [10,20,30].map(function (y,j) {
return x+y;
});
})
// result: [[110,120,130],[210,220,230],[310,320,330]]

Categories