« Back to home

No CoffeeScript for Beginners

CoffeeScript is growing in popularity. Taking a tour around the recent web repositories on Github, you'll notice a lot of CoffeeScript being used instead of our good old Javascript. There was a heated debate on whether CoffeeScript is worth it, and the best answer is "You either hate it or love it". However, I think CoffeeScript is designed for programmers who already know Javascript well. For beginners, it actually makes their Javascript learning journey quite a lot harder. CoffeeScript has too much behind-the-scene magic that makes some of the essential Javascript behaviors not very obvious. Let's take a look at 2 of the most important Javascript features, execution context and prototypal inheritance and why CoffeeScript is not a good learning tool in those cases.

Execution context

Execution context is basically the environment the current code is being evaluated in. It is the reason for some prominent Javascript features that are well-beautified by CoffeeScript to the point that they are almost hidden to beginners.

Global context

At the bottom of the execution stack is always the global context which can be accessed by all. The language's dependence on a global context is the ugliest thing about Javascript and should be used with caution. CoffeeScript tries to avoid accidentally adding things to the global context by automatically wrapping all the code in a function call:

(function() {
  // Your code...
}).call(this);

as well as automatically adding var to every variable declaration. They are good practices every Javascript programmers should follow, but have become a behind-the-scene magic in CoffeeScript. Lots of beginners don't understand why global context is bad and why CoffeeScript does it that way. I often see people who are too used to CoffeeScript struggle with when and where to use global context when switching back to Javascript. Some of them even forget to use var most of the time. The result is very nasty.

Scope chain and this

The this keyword in Javascript is one of the most confusing things in the language, and most people learn it the hard way. In a nutshell, the value of this is determined during the creation stage of the execution context and refers to the context in which the function is called. It gets more and more confusing in the case of event handler. For example:

var Foo = function() {
  this.handler = function() {
    console.log(this);
  }
}
var foo = new Foo();
button.onclick = foo.handler;

this in this case is the button element, not the foo instance. For the handler function to get access to foo`, we can make use of scope chain:

var Foo = function() {
  var _this = this;
  this.handler = function() {
    console.log(_this);
  }
}
var foo = new Foo();
button.onclick = foo.handler;

Now handler has access to _this which is a reference to foo. CoffeeScript adds some syntactic sugar to this technique by using arrow function:

Foo = ->
  this.handler = =>
    console.log(this)

It hides away scope chain and closure, which is bad for beginners who haven't fully understood the concepts. I've seen a lot of my friends do this:

foo = 
  handler: =>
    console.log(this)

button.onclick = foo.handler

Guess what? this now refers to the global context because we cannot define _this in object literal! So It's better to learn the real thing before trying to be smart.

Notes: ES6 is going to introduce built-in arrow function which will not have its this defined in execution context. It allows this to be lexically picked up from the outer context where the function is defined.

Variable hoisting

Another effect of execution context is variable hoisting. Most experienced Javascript programmers are aware that all variables declarations are hoisted to the top of the function. So this behavior is expected:

var foo = "Hello";
(function() {
  console.log(foo);  // undefined because declaration of `foo` is hoisted to the top
  var foo = "Hello World";
})();

CoffeeScript makes this a little easier by automatically taking care of variable duplication:

foo = "Hello"
( ->
  console.log(foo)
  foo = "Hello World"
  bar = "Test"
)()

This translates to:

var foo = "Hello";
(function() {
  var bar;
  console.log(foo);  // "Hello"
  foo = "Hello World";
  return bar = "Test";
})();

Again, this is good for those who already know about variable hoisting. For beginners, having too much magic behind the scene almost makes them blind to this essential Javascript behavior. I can imagine a lot of "WTFs" given when they face unexpected behaviors working on projects without CoffeeScript support.

Prototypal Inheritance

Understanding prototypal inheritance is the gateway to mastering Javascript. The CoffeeScript way of writing classes and inheritances is a double-edge sword for beginners. On one hand, it makes it easier for them to write functional object hierarchy. On the other hand, it deprives them from fully understanding the language's prototypal core. For example:

class Person
  money: 0
tom = new Person()
tom.hasOwnProperty('money')  // false

This looks pretty much like Java, except the fact that tom actually doesn't have money as its own property. Some of my friends with Java background had a very hard time understanding this behavior when looking at the code above. Compared to this native Javascript code:

var Person = function() { };
Person.prototype.money = 0;
tom = new Person();

It's much clearer now that money is added to the prototype of Person, not directly to tom. Every Javascript programmer must know that the language doesn't have the concept of classes. But CoffeeScript makes this really confusing by adding a too convenient class declaration syntax. The transition from classical to prototypal way of thinking is not going to be easy if inexperienced programmers keep using classes in CoffeeScript like that.

I've been advising my beginner friends to learn as much as possible about Javascript before writing anything in CoffeeScript (unless they don't care about Javascript and only want the shortcut). Even better, they should learn all of Javascript "the good parts" and how to write good Javascript code. After all, CoffeeScript is just the good parts of Javascript with some syntactic sugar. If we already write good Javascript code, CoffeeScript is just a matter of preference.

Comments

comments powered by Disqus