CoffeeScript FUNdamentals: The Better JavaScript

CoffeeScript FUNdamentals: The Better JavaScript

Disclaimer: This text is a part of the JavaScript and Node FUNdamentals: A Collection of Essential Basics ebook which is available now for free. However, upon the book’s completion it’ll be priced at $2.99. The book is 80% done as of this writing. The formats available: PDF, EPUB and Kindle. If you would like to participate in the writing process by providing your feedback and future topics, fill this short Future Topics and Feedback form.

CoffeeScript FUNdamentals: The Better JavaScript

The CoffeeScript is a language that was built on top of JavaScript. CoffeeScript has some added benefits and its code is compiled into native JavaScript for execution.
The CoffeeScript pros include: better syntax, function and class construction patterns, automatic var insertion, comprehensions and others.

Most of these perks will be obvious once we take a look at some examples. This quick language reference can get you started with CoffeeScript:

  • Semicolons, Whitespace and Parentheses
  • Vars
  • Conditions
  • Functions
  • Classes
  • Arrays
  • Splats
  • Comprehensions

Semicolons, Whitespace and Parentheses

While in JavaScript, semicolons are redundant and optional; in CoffeeScript they are banned.

The whitespace and indentation (typically two-space) are parts of the CoffeeScript language.

Parentheses for function invocation are optional (except when there are no arguments). The same goes for curly braces for object literals. We can even next objects without curly braces:

a =
  x: 1
  y: -20
  z: () ->
    console.log a.x+a.y

a.z()

b = [
  1,
  2,
    x: 10
    y: 20
]

Translates into this JavaScript:

var a, b;

a = {
  x: 1,
  y: -20,
  z: function() {
    return console.log(a.x + a.y);
  }
};

a.z();

b = [
  1, 2, {
    x: 10,
    y: 20
  }
];

As you might have noticed, the logical block’s curly braces that we use to write code for functions (i.e., {}) are also replaced by indentation. Let’s not forget that functions are just objects in JavaScript. :-)

Vars

CoffeeScript automatically inserts var keywords for us and prohibits manual usage of var. For example, a,b, and c variable declarations will have the var in the JavaScript code:

a = 10
b = 'x'
c = [1,2,3]

JavaScript code:

var a, b, c;

a = 10;

b = 'x';

c = [1, 2, 3];

CoffeeScript always puts vars at the top of the scope where this particular variable was encountered first. The scope is defined by the function or window. For example, the anonymous function d will have e scoped to it, because CoffeeScript first saw e inside of the function:

a = 10
b = 'x'
c = [1,2,3]

d = () ->
  e = a
  console.log e
d()

JavaScript output:

var a, b, c, d;

a = 10;

b = 'x';

c = [1, 2, 3];

d = function() {
  var e;
  e = a;
  return console.log(e);
};

d();

Conditions

Conditions are more readable by humans (English-like?) in CoffeeScript:

a = b = c = d = 1
if a is b or b isnt c and not c is d
  console.log 'true'
else
  console.log 'false'
var a, b, c, d;

a = b = c = d = 1;

if (a === b || b !== c && !c === d) {
  console.log('true');
} else {
  console.log('false');
}

So is is ===, isnt is !==, not is !, and is &&, and or is ||.

In CoffeeScript some fancy and arguably more readable constructions are possible:

console.log a if a is not null
if a isnt null then console.log a
if not a is null then console.log a
unless a is null then console.log a

Note: unless is just a shortcut for if not.

Functions

Functions in CoffeeScript are defined with arrows ()-> and and fat arrows ()=> (more on this later):

a = (x,y) -> console.log x+y
a(10,-5)

JavaScript code:

var a;

a = function(x, y) {
  return console.log(x + y);
};

a(10, -5);

Longer expressions can be on multiple lines using indentation, while the default values can be assigned right in the function signature (i.e., (name=value)):

a = (x, y, z=15) ->
 sum = x + y + z
 console.log sum
a(10,-5)
var a;

a = function(x, y, z) {
  var sum;
  if (z == null) {
    z = 15;
  }
  sum = x + y + z;
  return console.log(sum);
};

a(10, -5);

So back to the far arrow, it does two things:
1. Defines a function
2. Binds the new function’s scope to the current value of this

Remember that this is dynamically scoped, i.e., its meaning changes based on where it is situated in the code (what’s the scope). For example, if we have a jQuery event handler click, we might want to use this as the object in which we defined the handler, not as the DOM element to which the handler is bound.

For example, this CoffeeScript code will return window object both times (that’s what we want):

console.log @
$('div').click ()=>
  console.log @

The JavaScript code:

console.log(this);

$('div').click((function(_this) {
  return function() {
    return console.log(_this);
  };
})(this));

However, with single arrows it’s back to the DOM scope for the event handler, (this might be bad if unexpected):

console.log @
$('div').click ()->
  console.log @
console.log(this);

$('div').click(function() {
  return console.log(this);
});

Traditionally for the snippet above, without the CoffeeScript’s far arrows, you would see workarounds like these which use interim variables like that, self, or _this:

console.log(this);
var that = this;
$('div').click(function() {
  return console.log(that);
});

Classes

Classes are probably the most yummiest and the most complex and confusing feature in CoffeeScript. In JavaScript classes are absent at all! We use prototypes instead, so the objects inherit from other objects. We can also use factories, i.e., the functions that create objects.

However, if a developer wants to implement a class, it could be really tricky and often requires a good understanding of pseudo-classical instantiation patterns. This is not the case with CoffeeScript, which introduces class keyword. Inside of the class we can use constructor method and super call, for the initialization logic and the invocation of the parent’s methods correspondingly.

For example, we have a parent class Vehicle from which we extend two classes Compact and Suv. In these classes, we write custom move methods with the super call, that allows us to re-use the logic from the parent class Vehicle.

class Vehicle
  constructor: (@name) ->

  move: (meters) ->
    console.log @name + " moved #{meters} miles."

class Compact extends Vehicle
  move: ->
    console.log "Cruising..."
    super 5

class Suv extends Vehicle
  move: ->
    console.log "Speeding..."
    super 45

camry = new Compact "Camry"
caddi = new Suv "Cadillac"

camry.move()
caddi.move()

The console outputs this:

Cruising...
Camry moved 5 miles.
Speeding...
Cadillac moved 45 miles.

The JavaScript output is quite lengthy, so no wonder developers often prefer functional or other patterns:

var Compact, Suv, Vehicle, caddi, camry,
  __hasProp = {}.hasOwnProperty,
  __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };

Vehicle = (function() {
  function Vehicle(name) {
    this.name = name;
  }

  Vehicle.prototype.move = function(meters) {
    return console.log(this.name + (" moved " + meters + " miles."));
  };

  return Vehicle;

})();

Compact = (function(_super) {
  __extends(Compact, _super);

  function Compact() {
    return Compact.__super__.constructor.apply(this, arguments);
  }

  Compact.prototype.move = function() {
    console.log("Cruising...");
    return Compact.__super__.move.call(this, 5);
  };

  return Compact;

})(Vehicle);

Suv = (function(_super) {
  __extends(Suv, _super);

  function Suv() {
    return Suv.__super__.constructor.apply(this, arguments);
  }

  Suv.prototype.move = function() {
    console.log("Speeding...");
    return Suv.__super__.move.call(this, 45);
  };

  return Suv;

})(Vehicle);

camry = new Compact("Camry");

caddi = new Suv("Cadillac");

camry.move();

caddi.move();

Arrays and Slicing

Arrays in CoffeeScript can be defined just as they are in native JavaScript: arr = [1, 2, 3]. But we can do so much more with arrays in CoffeeScript! For example, we can use a range when defining an array (useful in iterators and comprehensions) and use slice:

arr = [1..10]
slicedArr = arr[2..4]
console.log arr, slicedArr

The console outputs:

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] [3, 4, 5]
var arr, slicedArr;

arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

slicedArr = arr.slice(2, 5);

console.log(arr, slicedArr);

Trivia fact: for array declarations with 20+ items (e.g., range of [0..20] and larger), CoffeeScript compiler will switch to the for loop.

Splats

Splats is a better way of using a variable number of arguments and arguments object (from native JavaScript):

a = (x...) ->
 sum = 0
 x.forEach (item) -> sum += item
 console.log sum
a(10,-5, 15)
var a,
  __slice = [].slice;

a = function() {
  var sum, x;
  x = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
  sum = 0;
  x.forEach(function(item) {
    return sum += item;
  });
  return console.log(sum);
};

a(10, -5, 15);

Spats work with invocations too. For example, our sum function from the previous example needs to treat the array not as a first element, but as all arguments:

a = (x...) ->
 sum = 0
 x.forEach (item) -> sum += item
 console.log sum

a [-5..50]...

The output is 1260. And the JavaScript:

var a, _i, _results,
  __slice = [].slice;

a = function() {
  var sum, x;
  x = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
  sum = 0;
  x.forEach(function(item) {
    return sum += item;
  });
  return console.log(sum);
};

a.apply(null, (function() {
  _results = [];
  for (_i = -5; _i <= 50; _i++){ _results.push(_i); }
  return _results;
}).apply(this));

Comprehensions

The last but not least topic is comprehensions. They are probably the most used feature in CoffeeScript and replace (or at least try to replace) all loops.

For example, a simple iteration over an array:

arr = [
  'x',
  'y',
  'z'
]

for a in arr
  console.log a

The console output is:

x
y
z

The compiled code:

var a, arr, _i, _len;

arr = ['x', 'y', 'z'];

for (_i = 0, _len = arr.length; _i < _len; _i++) {
  a = arr[_i];
  console.log(a);
}

As is the case with conditions, comprehensions might be reversed in order, e.g., console.log a for a in arr. Then, we can get an index which will be the second parameter, e.g., console.log a, i for a, i in arr outputs:

x 0
y 1
z 2

The when clause acts like a filter method; in other words, we can apply a test to the iterator:

arr = ['x', 'y', 'z']
console.log a, i for a, i in arr when a isnt 'y'

The console outputs:

x 0
z 2

To step with an increment we can use by: evens = (x for x in [0..10] by 2). In addition, for iterating over objects we can use of:

obj =
  'x': 10
  'y':-2
  'z': 50

coordinates = for key, value of obj
  "coordinate #{key} is #{value}pt"
console.log coordinates

The console output is:

["coordinate x is 10pt", "coordinate y is -2pt", "coordinate z is 50pt"]

The JavaScript code is:

var coordinates, key, obj, value;

obj = {
  'x': 10,
  'y': -2,
  'z': 50
};

coordinates = (function() {
  var _results;
  _results = [];
  for (key in obj) {
    value = obj[key];
    _results.push("coordinate " + key + " is " + value + "pt");
  }
  return _results;
})();

Conclusion

This CoffeeScript FUNdamentals is a concise overview that should highlight major pros of this language, which has many more useful features. We hope that classes, arrow function declaration, comprehensions, splats, and the clean syntax were enough to spark interest and lead to more exploration and experimentation with CoffeeScript.

Here’s the list of further CoffeeScirpt reading:

Author: Azat

Techies, entrepreneur, 20+ years in tech/IT/software/web development expert: NodeJS, JavaScript, MongoDB, Ruby on Rails, PHP, SQL, HTML, CSS. 500 Startups (batch Fall 2011) alumnus. http://azat.co http://github.com/azat-co

3 thoughts on “CoffeeScript FUNdamentals: The Better JavaScript”

  1. I have heard of CoffeeScript many times but never really looked at it. JavaScript has always been sufficient. This is the first time I am looking at CoffeeScript. It looks interesting.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.