Even if you’ve been a front-end developer for years and have done a fair amount of JavaScript programming throughout your career, there may be a few things you’ve missed out on from those delivery pressures piled on you in your day-to-day development life.
Let’s take a step back, pause, take a breath and review some tricks and best practices you can learn from looking at the compiled JavaScript code resulting from CoffeeScript.
Contents
Example Code
Here’s an example of CoffeeScript code and its JavaScript output.
CoffeeScript:
myName = "Andrew"
age = 30
names = [myName, "Jim", "Michael", "Micah", "Yvette", "Shawna"]
say = (name) ->
if name is "Andrew"
"Hi Chalkers!"
else
"Hi #{name}!"
for name in names
console.log say(name)
JavaScript:
(function() {
var age, myName, name, names, say, _i, _len;
myName = "Andrew";
age = 30;
names = [myName, "Jim", "Michael", "Micah", "Yvette", "Shawna"];
say = function(name) {
if (name === "Andrew") {
return "Hi Chalkers!";
} else {
return "Hi " + name + "!";
}
};
for (_i = 0, _len = names.length; _i < _len; _i++) {
name = names[_i];
console.log(say(name));
}
}).call(this);
Executing the above code will result in the following output in your console:
Hi Chalkers!
Hi Jim!
Hi Michael!
Hi Micah!
Hi Yvette!
Hi Shawna!
Let’s deconstruct some of the JavaScript output and ask questions about why things are the way they are and see what we can learn.
1. Don’t Pollute the Global Scope
Why wrap your code in something like this?
(function() {
//Your code code here
}).call(this);
First off, what is this code? It’s an anonymous function. The call
method afterward means this is a self-executing anonymous function. In other projects or examples, you may see it written like this:
(function() {
//Your code code here
})(this);
Or:
(function() {
//Your code code here
})(window);
The this
variable in the context of being ran in the browser is the window
object. This protects the global scope from being polluted by your variables. When you normally define values outside of a function
like:
var myName = "Andrew";
You can access them via the window
object like this window.myName;
.
The above variable will be the string of "Andrew."
Now, you may think, “Great! Here’s another way I can access my variables.” But it’s not that simple. When working on large projects with multiple people or even including external dependancies and frameworks, you can’t guarantee the variables you’re declaring won’t clash with their variables if you’re polluting the global scope. You can’t guarantee that everyone is using this best practice either.
In order to protect your variables from clashing with other variables and prevent any unintended behavior, you’d need to wrap them up with any additional code you want to access those said variables inside a self-executing anonymous function.
So this code:
(function() {
var myName = "Andrew";
console.log(myName);
}).call(this);
will output Andrew
in to the JavaScript console.
But:
(function() {
var myName = "Andrew";
console.log(window.myName);
}).call(this);
will output undefined
because the myName
variable is declared in the scope of the anonymous function and thus not binding it to the window
object.
The next time you’re writing a less-than-trivial JavaScript application, try wrapping your code in an anonymous function.
2. Declare Your Variables Together
Declaring variables all together like this is a neat trick.
var age, myName, name, names, say, _i, _len;
Not only are you saving on space writing var
only once but you can give others an idea about what your application is about with a quick glance. This may be a tad more digestible than scanning through all of the code to get an idea of what variables you can use.
However, in large complicated applications, you’d probably have several JavaScript files, all with their own self-executing anonymous functions that only deal with a specific set of code. This would avoid a long first line and confusing things for new developers to the project. Be modular! You can always combine these files for deployment later.
3. Operators Matter
Did you notice the line if name is "Andrew"
gets compiled down to if (name === "Andrew")
? Why did CoffeeScript use ===
rather than ==
?
When you use the ==
operator, JavaScript tries to coerce each of the operands (the variables either side of the operator) to match one another. This can lead to some very unexpected outcomes. Here are some mind-melting examples:
0 == ''; //true
1 == '1'; //true
false == ''; //true
false == []; //true
false == 'false'; //false
However, when you use ===
it compares the types of the operands first and then the values. This can be quicker operation than its ==
counterpart because the JavaScript interpreter doesn’t have convert the types first before comparing the objects. If they aren’t of the same type, it will automatically return false
, which is what you’d be expecting.
So if you’re confirming values, it’s best to use ===
to prevent any weird goings-on. If you want to look at some more examples, check out this Stack Overflow post.
If you’re ever doing any comparisons, it’s best to use ===
if you’re expecting the operands to be of the same type, which is the case in most instances.
Conclusion
Even if you don’t write much pure JavaScript anymore, it’s good to know these things as you never know when you’re going to need to get your hands dirty on an old code base or if the team you’re working on doesn’t use or want to use CoffeeScript.
I’ve always found that learning best practices in one language or framework helps introduce those in other situations.
Is there anything you’ve spotted in the JavaScript output by the CoffeeScript compiler that you’ve found interesting? Or have there been other things you’ve taken from learning one language and taken it into others?
The first thing you learn is that functions have scope. Using `var` declarations inside the function puts `myName`&Co in the local scope of the anonymous function. It means that if you had a `myName` variable declared outside the anonymous function, calling `myName = “Andrew”` inside the function would point to the `var myName` inside the function. A little thing called shadowing.
The second thing you learn is about strict mode. Since you mentioned `call(this)`. It’s for strict mode. In strict more, if the self executing function expression is just `(function{})(this)`, `this` is undefined. With `(function{}).call(this)` however, the `window` object gets to be the target object.
The third thing you learn about is not *type* coercion necessarily, but the fact that in JavaScript except for the following literals: false, 0, “” (empty string), null, undefined, NaN everything else is a truthy value: ‘false’ is a not an empty string so it evaluates to `true`, `Boolean(false)` is an object so it evaluates to `true`, how’s that for mind bending.
Related to #2 – It’s not just about semantics, there’s an important functional reason too: hoisting.
In JS, variable declarations get ‘hoisted’ to the top of a function, but their initializations don’t. As an example:
var foo = ‘something’;
(function(){
console.log(foo); // prints ‘undefined’
var foo = ‘something else’;
})();
Coffeescript printing variable names at the top of a function isn’t just to save space, it’s how the browser actually interprets variable initializations, so the output serves as a good reminder of this.
For anyone relatively new to JS, I highly recommend looking into hoisting – there are plenty of good articles on the topic.
Good point!
The space savings wouldn’t mean too much for a gzipped JS file either!