Why is CoffeScript not "reusing" loop variable? - javascript

I am new to CoffeeScript and very excited about it. I made some basic loops here. Now, CoffeeScript is defining a loop variable for every loop there is as follows:
var food, _i, _j, _len, _len1;
for (_i = 0, _len = fruits.length; _i < _len; _i++) {
food = fruits[_i];
console.log(food);
}
for (_j = 0, _len1 = vegetables.length; _j < _len1; _j++) {
food = vegetables[_j];
console.log(food);
}
I used to code my loops like this:
for(var i = 0; i < fruits.length; i++) {
console.log(fruits[i]);
}
for(var i = 0; i < vegetables.length; i++) {
console.log(vegetables[i]);
}
i was my loop variable for every loop (nested loops excluded). Now I learned that you should always declare your variables before defining it. So I changed my coding habits to:
var i;
for(i = 0; i < fruits.length; i++) {
console.log(fruits[i]);
}
for(i = 0; i < vegetables.length; i++) {
console.log(vegetables[i]);
}
As long as I am in the same scope I did not see anything wrong with it, but the compiled CoffeeScript code left me wondering.
Why would CoffeeScript use a different variable for every loop?

Without having looked at the CoffeeScript source code, here's my (educated) guess:
The CoffeeScript interpreter simply creates a new for loop construct for every for .. in you write. While constructing output JS, it maintains a table of local variable names and appends to that as necessary.
After all, you could nest these for .. in loops, in which case you'd start to need separate loop variables in JS anyway.
It would be necessary to track which variables in a local function scope could be potentially re-used. This is possible, but more complex than it's worth - it simply does not provide any benefit, so CoffeeScript does not do it.

The other side of this is, CS can't count on you only using the variable inside of the loop. While it could be dangerous to rely on your iteration variable for use outside the loop, it's possible you will want to. CS reusing it wouldn't give you that chance, if it always used the same variable.

Related

When updating some of the elements in an object array in Javascript, which one is a more efficient way considering the array size?

Consider this very simple js code below:
for(var i = 0; i < rows.length; i++) {
if(rows[i].index !== i) {
rows[i].index = i;
}
}
Say, the length of the array is 8, and it will enter the if block 2 times. Is it better to do this way:
for(var i = 0; i < rows.length; i++) {
rows[i].index = i;
}
I want to know which one is less costly with large arrays and small arrays; the if block, or the value assign in every cycle of the loop?
it shouldn't really matter. I still tried it on jsPerf for the sake of curiousity and it seems that the second version is faster.

Why does CoffeeScript compile to unidiomatic JavaScript?

I'm playing around with Coffee script (I'm new to this "language") and tried just a very basic example:
x = [1, 2, 3]
for element in x
console.log(element)
This does what it says, for each of the elements it outputs it to the console. It's only when I took a lot at the Javascript it compiled to that I can't understand why they do this:
(function() {
var element, i, len, x;
x = [1, 2, 3];
for (i = 0, len = x.length; i < len; i++) {
element = x[i];
console.log(x[i]);
}
}).call(this);
Would it not just be better and more natural to do:
for(i = 0; i < x.length; i++)
{
console.log(x[i]);
}
Why does coffee script compile to such a way that does not seem natural? If I was writing the same exact code in Javascript, I would follow the method that I did, but I might be wrong in that the way I did it (Javascript) is not the most efficient or "natural" and the coffee script method is a better solutions.
Basically you are expecting the CoffeeScript compiler to do acrobatics that would be really complicated.
Consider this CoffeeScript:
for a in x
console.log(a)
for b in a
console.log(b)
callFunc(b)
This transpiles to:
for (i = 0, len = x.length; i < len; i++) {
a = x[i];
console.log(a);
for (j = 0, len1 = a.length; j < len1; j++) {
b = a[j];
console.log(b);
callFunc(b);
}
}
It's very easy to see how the original CoffeeScript corresponds to the resulting JavaScript:
for [variable] in [array]
[body]
becomes
for (var [letter] = 0, len = [array].length, [letter] < len; [letter]++) {
[variable] = [array][[letter]]
[body converted to JavaScript]
}
So when translating a for comprehension into JavaScript, (almost) all the compiler needs to worry about is this simple correspondence, and then it can work inward and process the body as a separate and independent operation.
Also, the len = [array].length caching is done for performance. This isn't entirely pleasant to look at, and most people wouldn't code this way, but producing beautiful JavaScript is not the primary goal of CoffeeScript. It produces efficient code with the assumption that people won't generally be reading the generated JavaScript.
What you are suggesting is that it take that original code and turn it into:
for (i = 0; i < x.length; i++) {
console.log(x[i]);
for (j = 0; j < x[i].length; j++) {
console.log(x[i][j]);
callFunc(x[i][j]);
}
}
In order to convert console.log(b) into console.log(b);, the compiler doesn't need to know anything about the code surrounding that statement. In order to convert console.log(b) into console.log(x[i][j]);, the compiler would have to take everything around that statement into account. It would give the compiler an extremely complicated job to perform and wouldn't really provide any benefit.
Firstly, CoffeeScript builds everything in a closure by default, that would be these lines:
(function() {
}).call(this);
Closures prevent variables leaking out of the script. If you want to export your script as a global, then you need to explicitly attach it go a global, like window.
Next is the line:
var element, i, len, x;
It is considered good practice to declare your var's before using them (at least according to jslint).
Your definition of x of course:
x = [1, 2, 3];
Now the loop:
for (i = 0, len = x.length; i < len; i++) {
element = x[i];
console.log(x[i]);
}
Defining len first prevents the loop constantly looking up what x.length is. Not a huge speed difference with only three items, but an array with 10k, well...
The console.log is obvious, but the element variable is defined because that is the name of the variable you specified when you wrote your loop. And it uses i instead of element to iterate mostly because it's standard practice to use i for iteration.
So you see everything has a legitimate reason, albeit a little verbose for some tastes.

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.

Variable scope in javascript when iterating through a stack

I'm baffled. Could someone please explain to me why this produces an infinite loop?
var constant = 4;
var stack = new Array();
stack.push(0);
stack.push(1);
loop1();
function loop1(){
for(i = 0; i < constant; i++){
loop2(i);
}
}
function loop2(num){
for(i = 0; i < stack.length; i++){
console.log(i);
}
}
​
Fiddle: http://jsfiddle.net/elclanrs/tywV9
I suspect it has something to do with Javascript function-level variable scope, but that's as far as my guess goes.
When you don't declare a variable with the var keyword, the variable is global, so loop2 and loop1 are using the same (global) i variable. Every time loop2 is called, i gets set to 0 and then is incremented up to stack.length, which is 2. This means that every iteration of loop1 will end with i=2, which gets incremented to 3 but never reaches constant which is 4.
If you change your for loops to be for (var i = 0; ...) instead of for (i = 0; ...) then this should no longer loop infinitely.

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