I was just writing two JavaScript functions, one of which took in a long string, looped over it until it hit a space, then called the other function to print the input before the space into the DOM. The first function would then continue on with input after the space, hit a space, call the print function, etc.
In the process, I kept hitting infinite loops, but only if the string contained a space. I couldn't figure out why, since all the looping seemed to be set up properly. I ultimately figured out that my iterator variable was jumping scope out of my second function printMe and back into the first, readAndFeed, and because of the way the functions were set up, it would always come back as a lower number than the terminating value if there was a space involved.
The first function's loop looked like this:
function readAndFeed(content){
var output = "";
var len = content.length;
for(i = 0; i < len; i++)
{
console.log(i+" r and f increment")
if(content[i] == (" "))
{
printMe(output);
output = "";
}
else if(i==len-1){
output += content[i];
printMe(output)
}
else
{
output += content[i]
}
}
}
The second function is printMe(), and it looped over the string, broke it into three bits, looped over each of them separately (not in a nested fashion), and printed them to the DOM. I used similar loops in it, and I also used i as an iterator.
This would loop over strings with no spaces just fine, but if I threw a space in there, the browser would crash. I tried a bunch of different stuff, but ultimately (by logging the iterator values) realized something was up with i. What worked was changing the i in the printMe function to a j.
I'm confused; this doesn't seem like how I understand variable scope. The functions are defined separately, so it seems like the iterators should be local to those functions and not able to jump out of one into the other.
Here's a jsfiddle
Uncomment the "is an example" part at the bottom to crash your browser. Again, changing the i variables to j in the printMe function completely solved this, but whaaa?
When you don't declare a variable, it is implicitly global. Since you've not declared your loop iteration index i, it is global. If you do that in multiple functions, those globals will collide and one function will accidentally modify the other's variable.
The solution is to make SURE your local variables are declared with var as in:
for (var i = 0; i < len; i++) {
In your case, you need to fix both readAndFeed() and printMe() as they both have the same issue and thus they both try to use the global i. When you call one from the other, it trounces the original's use of i. Here's a fixed version of readAndFeed():
function readAndFeed(content) {
var output = "";
var len = content.length;
// add var here before i
for (var i = 0; i < len; i++) {
console.log(i + " r and f increment")
if (content[i] == (" ")) {
printMe(output);
output = "";
} else if (i == len - 1) {
output += content[i];
printMe(output)
} else {
output += content[i]
}
}
}
If you run your Javascript code in strict mode, then trying to use an undeclared variable actually causes an error (rather than implicitly make it a global) so you can't accidentally shoot yourself in the foot like this.
In your example, i is in fact a global variable. Any assignment without var statement to an undeclared variable declares an implicit global.
To make i local, just include var:
for (var i = 0; i < len; i++)
Related
I have a for loop which will iterate through all choices and and set a value for them.
For some reason, when I run the code it does the first iteration, then skips to the last and does that one 3 times, or so according to the console.
code:
for (i = 0; i < 4; i += 1) {
console.log(i)
var generated = word
while (generated == word) {
generated = wordsJson.characters[Math.floor(Math.random() * wordsJson.characters.length)]
}
choices[i].innerHTML = translate(generated)
}
What I get in console:
0
(3) 3
This is my first time asking something on stackoverflow. If you need more information, please ask.
It appears that the variable i is getting modified outside of the for loop.
Typically you would want to declare your iterator variable (in this case i) so that it's scoped to the loop, which would look like:
for (let i = 0; i < 4; i += 1) { ... }
Note, specifically, the addition of let. Since you haven't done that, it means that either i is already explicitly declared somewhere or, if not, that you've created a new global variable i.
Since you've got code you haven't shown that also seems to relate to i (choices[i]) and methods that we don't know the exact function of (translate()) it's hard to say for certain, but that would be the first place to look.
If not, posting some additional code so we can see the other functionality would be helpful.
When I am adding values normally it's getting added to json:
Code:
for (var k = 0; k < result.length; k++) {
result[k]["sender"] = k;
}
But when I call value from api and try to store it in result[k]["sender"] then it's not saving in json and values are getting printed on console:
for (var k = 0; k < result.length; k++) {
var url = "//api";
$.ajax({
url: "https://reverse.geocoder.api.here.com/6.2/reversegeocode.json?prox="+result[k]['lat'].toString()+","+result[k]['lon']+"&mode=retrieveAddresses&app_id=***&app_code=***",
context: document.body,
success: function (data) {
console.log(k, typeof data['Response']['View'][0]['Result'][0]['Location']['Address']['Label']);
result[k]["sender"] = data['Response']['View'][0]['Result'][0]['Location']['Address']['Label'];
}
});
}
Error:
Can anyone guide me what I'm missing?
Change var k to let k.
k is incremented at each iteration of the loop, and its final value will be result.length at which point the loop stops.
Your loop first runs for result.length times, incrementing k every time, and you start all those requests with $.ajax. Note, you only start them here, as they are asynchronous! That means, the time when they complete and success is called will be later on, and meanwhile the rest of the code will continue running.
So, later on, the requests complete, and they attempt to access result[k]. However, k === result.length at this point (because the loop has long finished running), so result[k] will be undefined.
With let you can make use of a special feature in ES6 where a let variable inside a for statement actually creates two instances of the variable, with the first one actually counting and the second one being reinitialized to the current value of the first and made available to the inner scope (but keeping its value inside the closure). That's a simplification, but the bottom line is that this way each iteration of your loop will have its own k which keeps its value even after the loop advanced.
Note: Your url seems static at the moment, but if it depends on k at some point then it should also be let, otherwise you get the same problem there as well.
An alternative way, for example if you can't use ES6, would be to create a new (function) scope using an IIFE (immediately-executed function expression) into which you pass the variable as argument:
for (var k = 0; k < result.length; k++) {
(function (k) {
// Do $.ajax and everything else here
})(k);
}
Or, you could use forEach (which takes a callback so it will automatically have a new variable every time).
I have never seen a JavaScript loop such as this for( ; i-- ; ), used in the code:
uid: function (len) {
var str = '';
var src = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
var src_len = src.length;
var i = len;
for (; i--;) {
str += src.charAt(this.ran_no(0, src_len - 1));
}
return str;
}
I understand the behavior, but I would like it if somebody could share some insights about this type of for loop.
This is a syntax of the for-loop construction:
for ([initialization]; [condition]; [final-expression])
statement
In your case for (; i--;) {:
no variables are initialized, because var i = len; inintialized earlier, so it's not needed.
condition part will be truthy until i becomes 0 then loop will terminate. i-- is executed on before each iteration, and due to -- operator it will eventually become 0, so it's falsy, and loop will stop.
since i is decremented in condition part of the loop, final-expression is not needed too. Another way to put it: since i is not used inside the loop, it does not matter whether we decrement it before each loop iteration or after each loop iteration.
That being said, it's better to avoid writing loops like above, as it's pretty confusing and hard to read. Prefer traditional for-loops notation.
From MDN - for - Optional for expressions:
All three expressions in the head of the for loop are optional.
You don't have to specify all three expressions in for loops. For example, for (;;) is a common wa of writing infinite loop.
In your case, while(i--) would have done the same, there is no good reason to write for (; i--;).
I'd also note that for(var i=len;i>=0;i--) is more robust - it protects you from the case len is negative.
This could be rewritten to
uid: function (len) {
var str = '';
var src = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
var src_len = src.length;
var i = len;
while (i >= 0) {
str += src.charAt(this.ran_no(0, src_len - 1));
i = i - 1;
}
return str;
}
The for statement creates a loop that consists of three optional
expressions.
Javascript consider 0 == false that's why in the case you presented the loop will run until the i variable became zero. It will loop as many times as the src string size.
Note: i-- uses the variable value then decrements it. Take a look at the following situation:
for(;i--;) { // i = length in the condition
// i === length - 1 here. It will not overflow the array
}
for(;i--;) { // i = 1
// i === 0 here. It will be the last loop
}
for(;i--;) { // i == 0 == false
// Not executed
}
There is nothing wrong.
for(`INIT`;`TEST`;`ACTION`)
{
`WORK`;
}
The INIT (initialization) can be done outside the loop.
var i=0;
for(;i<=100;i++)
//do something
The TEST part yield a result that is either true or false. Now in this case value of i is tested. Until it becomes zero this works.
The ACTION part is generally used to change the loop variable. But you can leave it also or probably add it to the TEST section like it is done here.
Look this examples are going to clear your idea
var i=0;
for( i++; i++; i+=j); //No body
var i=0;
for(;;); //an infinite loop
var i;
for(i=-4;i;i++);//
Even sometimes WORK is placed in ACTION.
Example:
factorial of x
for(var i=1,ans=1;i<=x;ans=ans*(i++));
Which can be written this way also-
var ans=1;
for(var i=1;i<=x;i++)
ans=ans*i;
NOTE: You can write whichever way you want. It doesn't matter as long as you have written the code properly. Get used to this kind of form you will see them a lot.
Though, it is better to write sometimes in compact form , but remember you should keep the readability.
That's just for loop. Before the first semicolon the variable used in the loop is usually declared. But in this case the variable, the variable used in the loop is declared earlier:
var i = len;
^^^
for (; i--;) {...
So there is no need to redeclare.
After the first semicolon, there is a condition for the loop to run (i.e. i<6, i>3, etc). If condition returns false, or 0, the loop is exited. In this case, the loop will be broken out when i is 0. It happens eventually, because -- after i decrements it, and therefore there is no need for the expression, which is the place after the second semicolon is reserved for.
The first parameter of the for loop has already been defined, so the initial comma is there to delimit it's place in the parameter list.
If you have a loop set up like
for (var i=0, t=myArray.length; i < t; i++)
once that loop is complete, is it ok to use the i and t vars in the next non-nested loop within the function?
I've been numbering my nested loops differently like
for (var i2=0, t2=anotherArray.length; i2 < t2; i2++)
but also doing the same for non-nested loops. I'm wondering if there's a need?
I ask, as Flash doesn't like it when you use the same vars, regardless if the second loop is not nested.
Thanks for your time and help.
Normally loop variables aren't used for anything other than doing calculations within the loop.
Even though the variable is available outside the loop, normally it isn't used.
Sometimes you might set another variable to the exit value of a loop variable.
for(i=0;i<max;i++){
....
if (some exit condition that leaves the loop early){
//should assign value here instead of outside the loop
exitvalue = i;
break;
}
}
//exitvalue = i; //can assign the value of i here (try to avoid this)
Therefore it is usually quite safe to reuse loop variables.
The var i or t is declared in the scope where the loop is in.
Javascript uses "Hoisting".
So,
for (var i=0, t=myArray.length; i < t; i++) // first loop
is equal to:
var i,t;
for (i=0, t=myArray.length; i < t; i++)
So, when you use the same variables next time in a non-nested loops, like this:
for (var i=10, t=myArray.length; i < t; i++) // second loop
the i and t are already hoisted and hence you are just doing something like this:
var i = 0;
i = 10;
So it is okay to use i and t vars in the next non-nested loop.
I was just linting some JavaScript code using JSHint. In the code I have two for-loops both used like this:
for (var i = 0; i < somevalue; i++) { ... }
So both for-loops use the var i for iteration.
Now JSHint shows me an error for the second for-loop: "'i' is already defined". I can't say that this isn't true (because it obviously is) but I always thought this wouldn't matter as the var i is only used in that specific place.
Is it bad practice to use for-loops this way? Should I use a different variable for each for-loop in my code like
//for-loop 1
for (var i = 0; ...; i++) { ... }
//for-loop 2
for (var j = 0; ...; j++) { ... }
Or is this on e of the errors I can ignore (because it doesn't break my code, it still does what it is supposed to do)?
JSLint btw. stops validating at the first for loop because I don't define var i at the top of the function (that's why I switched to JSHint in the first place). So according to the example in this question: Should I use JSLint or JSHint JavaScript validation? – I should use for-loops like this anyway to confirm JSLint:
...
var i;
...
//for-loop 1
for (i = 0; ...; i++) { ... }
...
//for-loop 2
for (i = 0; ...; i++) { ... }
This also looks good to me, because this way I should avoid both errors in JSLint and JSHint. But what I am uncertain about is if I should use a different variable for each for-loop like this:
...
var i, j;
...
//for-loop 1
for (i = 0; ...; i++) { ... }
//for-loop 2
for (j = 0; ...; j++) { ... }
So is there a best practice for this or could I just go with any of the code above, meaning I choose "my" best practice?
Since variable declarations are hoisted to the top of the scope in which they appear the interpreter will effectively interpret both versions in the same way. For that reason, JSHint and JSLint suggest moving the declarations out of the loop initialiser.
The following code...
for (var i = 0; i < 10; i++) {}
for (var i = 5; i < 15; i++) {}
... is effectively interpreted as this:
var i;
for (i = 0; i < 10; i++) {}
for (i = 5; i < 15; i++) {}
Notice that there is really only one declaration of i, and multiple assignments to it - you can't really "redeclare" a variable in the same scope.
To actually answer your question...
is there a best practice for this or could I just go with any of the code above?
There are varying opinions on how best to handle this. Personally, I agree with JSLint and think the code is clearer when you declare all variables together at the top of each scope. Since that's how the code will be interpreted, why not write code that looks as it behaves?
But, as you've observed, the code will work regardless of the approach taken so it's a style/convention choice, and you can use whichever form you feel most comfortable with.
It has been mentioned only in the comment by #TSCrowder: If your environment supports it (Firefox, Node.js), in ES6 you can use let declaration
//for-loop 1
for (let i = 0; ...; i++) { ... }
//for-loop 2
for (let i = 0; ...; i++) { ... }
which limits the scope to within the for-loop. Bonus: JSHint stops complaining.
Variables in javascript are function scoped (not block scoped).
When you define var i in a loop, it remains there in loop and also in the function having that loop.
See below,
function myfun() {
//for-loop 1
for (var i = 0; ...; i++) { ... }
// i is already defined, its scope is visible outside of the loop1.
// so you should do something like this in second loop.
for (i = 0; ...; j++) { ... }
// But doing such will be inappropriate, as you will need to remember
// if `i` has been defined already or not. If not, the `i` would be global variable.
}
The reason JSHint shows the error is because in JS variable scope is function and variable declarations are hoisted to the top of the function.
In Firefox you can use let keyword to define block scope, but is not currently supported by other browsers.
The let keyword is included ECMAScript 6 specification.
I know this question has been answered, but if you want super for loops, write them like this:
var names = ['alex','john','paul','nemo'],
name = '',
idx = 0,
len = names.length;
for(;idx<len;++idx)
{
name = names[idx];
// do processing...
}
A couple of things going on here...
The array length is being stored in len. This stops JS evaluating names.length every iteration
The idx increment is a PRE-INCREMENT (e.g. ++idx NOT idx++). Pre-increments are natively faster than Post-increments.
The storing of a reference to name. This is optional but recommended if you'll be using the name variable a lot. Every call to names[idx] requires finding the index in the array. Whether this search be a linear search, tree search or hash table, the find is still happening. So store a reference in another variable to reduce lookups.
Finally, this is just my personal preference, and I have no proof or any performance benefits. However I always like initialising variables to the type they're going to be e.g. name = '',.
The best practice is to reduce the scope of variables, so the best way to declare iteration variable for the loops is
//for-loop 1
for (var i = 0; ...; i++) { ... }
//for-loop 2
for (var j = 0; ...; j++) { ... }
I know the scope of the variables declared with var but I am taking about code readability here.