Why does CoffeeScript compile to unidiomatic JavaScript? - 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.

Related

Using var to intentionally leak a variable

Is the following pattern ok in javascript, or is this frowned upon? And if the latter, what would be a better approach?
function arithmetic_sum(n) {
for (var [i, sum] = [0, 0]; i <= n; sum += i++);
return sum;
}
console.log(arithmetic_sum(10));
// 55
This is valid JavaScript.
Some people may like that, but it's a bit outdated and mixing old and new styles (var, but in combination with destructuring).
Also having code inside the interation clause of the for loop can be confusing.
I would prefer this instead:
function arithmetic_sum(n) {
let sum = 0;
for (let i = 0; i <= n; i++) {
sum += i;
}
return sum;
}
console.log(arithmetic_sum(10));
// 55
It really depends on the person to be honest. Some people will like this pattern because it makes the code shorter but in my personally opinion it makes the code harder to understand.
The reason this works is because of javascript hoisting. You can read more about this here.
https://www.w3schools.com/js/js_hoisting.asp
Now, if you were to switch this code to use let this code would break.
I think its also important to understand how var differs from let & const.
This article explained the differences very good
https://medium.com/#codingsam/awesome-javascript-no-more-var-working-title-999428999994
I personally would write this code like this:
function arithmetic_sum(n) {
let sum = 0;
for (let i = 0; i <= n; i++) sum += i;
return sum;
}
console.log(arithmetic_sum(10));
// 55

Loop optimization issues in node.js and chrome?

I happened to see the saying that avoiding to read the length attribute of an array at every iteration saves execution time.
But I don't think it matters and made an experiment.
Then I met the questions.
Here's the code.
// Function to get the execution time.
function testFunction (func) {
console.time(func.name)
for (let i = 0; i < 100; i++) {
func()
}
console.timeEnd(func.name)
}
// Init the array to iterate.
let arr = []
for (let i = 0; i < 10000000; i++) {
arr.push(0)
}
function loopWithSavedLength () {
let len = arr.length
for (let i = 0; i < len; i++) {
arr[i] = i
}
}
function loopWithoutSavedLength() {
for (let i = 0; i < arr.length; i++) {
arr[i] = i
}
}
testFunction(loopWithoutSavedLength)
testFunction(loopWithSavedLength)
And the output is very strange:
loopWithoutSavedLength: 889.633ms
loopWithSavedLength: 1023.269ms
I tried many times under Node.js 9.8.0(with v8 6.2.414.46-node.21) and the loopWithoutSavedLength execution time is always shorter than loopWithSavedLength's.
I execute the same script in chrome 66.0.3359.181(with v8 6.6.346.32) console and they are barely the same.
loopWithoutSavedLength: 1475.060302734375ms
loopWithSavedLength: 1493.14892578125ms
Then I thought it might be issue with assignment and test empty loop.
Here is new code.
function assignmentLoopWithSavedLength () {
let len = arr.length
for (let i = 0; i < len; i++) {
arr[i] = i
}
}
function assignmentLoopWithoutSavedLength () {
for (let i = 0; i < arr.length; i++) {
arr[i] = i
}
}
function emptyLoopWithSavedLength () {
let len = arr.length
for (let i = 0; i < len; i++) {}
}
function emptyLoopWithoutSavedLength () {
for (let i = 0; i < arr.length; i++) {}
}
testFunction(emptyLoopWithSavedLength)
testFunction(emptyLoopWithoutSavedLength)
testFunction(assignmentLoopWithSavedLength)
testFunction(assignmentLoopWithoutSavedLength)
The results in node.js:
emptyLoopWithSavedLength: 580.978ms
emptyLoopWithoutSavedLength: 584.923ms
assignmentLoopWithSavedLength: 1046.899ms
assignmentLoopWithoutSavedLength: 901.542ms
The results in chrome console:
emptyLoopWithSavedLength: 584.126953125ms
emptyLoopWithoutSavedLength: 892.776123046875ms
assignmentLoopWithSavedLength: 1455.418212890625ms
assignmentLoopWithoutSavedLength: 1449.7529296875ms
Later I realized the value stored in arr may influence the consequence and it does.
New code here:
let arr = []
function initArr () {
arr = []
for (let i = 0; i < 10000000; i++) {
arr.push(0)
}
}
function testFunction (func) {
initArr()
console.time(func.name)
for (let i = 0; i < 100; i++) {
func()
}
console.timeEnd(func.name)
}
The result in node.js:
emptyLoopWithSavedLength: 560.739ms
emptyLoopWithoutSavedLength: 1134.274ms
assignmentLoopWithSavedLength: 1841.544ms
assignmentLoopWithoutSavedLength: 1609.649ms
The result in chrome console:
emptyLoopWithSavedLength: 592.8720703125ms
emptyLoopWithoutSavedLength: 910.886962890625ms
assignmentLoopWithSavedLength: 1457.467041015625ms
assignmentLoopWithoutSavedLength: 1488.855224609375ms
Now new question comes up.
In conclusion:
1. In node.js, why is loopWithoutSavedLength time always smaller than loopWithSavedLength no matter array's elements are initialized to 0 or not?
2. Why does creating new array and initializing its elements to 0 make a different consequence?
V8 developer here.
Question 1: loopWithoutSavedLength is faster because accessing an element needs to perform a bounds check, which needs to load the length anyway. It can be eliminated if the loop's condition already contains the same check. It is much harder for the compiler to eliminate the additional check if you save the length. So in effect, saving the length manually means that work is duplicated. That said, the difference is usually too small to matter (or even be measurable). See the excellent writeup at https://mrale.ph/blog/2014/12/24/array-length-caching.html for more details.
Question 2: I'm not sure. The values of the elements shouldn't matter; and in Chrome they don't. V8 in Node.js acts the same and should perform the same as in Chrome; but maybe there's a difference between the two V8 versions you've tested. I'm confused by the results you got for emptyLoopWithoutSavedLength in Node.js, which apparently went from 584 to 1134 for no good reason at all -- maybe something else in your system caused a temporary slowdown? Can you reproduce this result?
Speaking of reproducing: when I run these tests repeatedly, I see performance differences of about 10% between repeated runs of the very same test (e.g. the results I got for assignmentLoopWithSavedLength over the course of 10 runs were between 1075 and 1222). That's not unusual; modern computers are complex machines with many layers and many sources of performance variations. It just means that when you do a single run of two tests, and you see 1400 and 1450 milliseconds, that might not mean anything -- next time around they might swap results. If you see 1455 and 1449, the difference is almost certainly noise.

What is the most idiomatic way to handle variables declared in multiple for loops? [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 9 years ago.
Improve this question
JavaScript only has function scope. Thus, variables declared in for loops are visible for the entire function.
For example,
function foo() {
for(var i = 0; i < n; i++) {
// Do something
}
// i is still in scope here
}
When we have multiple for-loops, this opens the question of how we handle the variables in these other for loops.
Do we use a different variable?
for(var i = 0; i < n; i++) { }
for(var j = 0; j < n; j++) { }
Or do we use the same variable but just assign a value (instead of declaring it)?
for(var i = 0; i < n; i++) { }
for(i = 0; i < n; i++) { }
Or declare i outside of the loops?
var i;
for(i = 0; i < n; i++) { }
for(i = 0; i < n; i++) { }
Or redeclare i?
for(var i = 0; i < n; i++) { }
for(var i = 0; i < n; i++) { }
All of these work (or at least they do on the latest versions of my browsers). JSHint doesn't like the last approach, though.
Is there an approach which is most idiomatic or otherwise preferable?
It really depends on who you're coding for. If you're coding for a company or contributing to a library you of course follow their style guide. I've seen all of these (expect the last) used in libraries. If you like the Douglas Crockford style you'll go with the second to last and place all your variables at the top of function scope (or jslint will shout at you).
Taking an example from the jQuery style guide:
This is considered Good style
var i = 0;
if ( condition ) {
doSomething();
}
while ( !condition ) {
iterating++;
}
for ( ; i < 100; i++ ) {
object[ array[ i ] ] = someFn( i );
}
While this is poor style:
// Bad
if(condition) doSomething();
while(!condition) iterating++;
for(var i=0;i<100;i++) object[array[i]] = someFn(i);
Anyway, because this is style I'm going to reference how several libraries write their for each loops:
jQuery uses the Crockford style in core as does lodash
Underscore js uses the last style as seen here. Mootools also uses this style
If your code is going to be minimized before you release it, it will not matter as minifiers will mangle it to pretty much the same end representation in the processing.
Using different variables we have no problems.
Reusing and reassigning makes the code less readable, and if we remove the declaration at a later time, we risk assigning i to something outside of the function scope.
Declaring i outside the loops, we have no problems.
Redeclaring will be an issue if your lint tool, IDE, etc complain.
So I would argue for the first or third option. If number of variables is a concern using the first option, then you are may be in need of a refactoring.
Another take that answers the question in a different way.
A function with multiple loops makes me suspicious because:
It may be doing too much and should be decomposed anyway, and
It may be better to write it more functionally and eliminate the index altogether (it's available in some each-/map-y functions anyway)
Another approach is to use iterator functions. For example, in modern browsers an Array will have a forEach method:
var items = ["one", "two", "three"];
var things = ["hello", "goodbye"];
items.forEach(function (item, index) {
// Do stuff
});
things.forEach(function (item, index) {
// Do stuff
});
If you're using older browsers (or custom collections), you can make your own iterator like this:
Array.prototype.forEach = function(callback) {
for(var i = 0; i < this.length; i++) {
callback.apply(this, [this[i], i, this]);
}
};
For more information see: Array.prototype.forEach()
Any variables declared within a function are interpreted as being declared at the beginning of the function. Doug Crockford argues that you should declare all of your variables at the first line of every function.
doSomething = function() {
var i, ... other variables ...;
...
for (i = 0; i < x; i += 1) {
...
}
...
for (i = 0; i < x; i += 1) {
...
}
}
This way the code reads in the same way it will be parsed by the javascript engine.

Why is CoffeScript not "reusing" loop variable?

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.

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