Skip to content

Instantly share code, notes, and snippets.

@codebycliff
Created June 17, 2013 21:57
Show Gist options
  • Save codebycliff/5800853 to your computer and use it in GitHub Desktop.
Save codebycliff/5800853 to your computer and use it in GitHub Desktop.
The JavaScript generated for a simple CoffeeScript class with explanations / annotations.
/* CoffeeScript:
class Person
constructor: (@name, @age) ->
@createdAt = new Date()
isAdult: => @age > 18
talk: => console.debug("Hello. My name is #{@name} and I'm #{@age} years old.")
*/
// Anonymously invoked function serving as namespace that creates a new scope
(function() {
// Global variable that will store the Person constructor function
var Person,
// Global function that binds the specified function (fn) to the specified scope (me).
__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
// Anonymously invoked function serving as a class definition using a closure
Person = (function() {
function Person(name, age) {
// Set properties
this.name = name;
this.age = age;
// Bind prototype functions to the scope (in this case, the Person function).
//
// This means that inside the prototype function, the keyword 'this'
// (a.k.a. the scope) refers to value of 'this' inside this Person
// function, which allows the prototype function to have access to
// whatever is defined on 'this' inside this Person function.
//
// Without the lines below (just the prototype definitions), the
// scope (the value of the keyword 'this') could refer to anything.
// For example, looping using jQuery's each function:
//
// var bob = new Person('Bob', 44);
// $('a').each(bob.talk);
//
// The value of the keyword 'this' (a.k.a. the scope) during the
// execution of the talk method would refer to the jQuery object
// for the anchor. This means it would blow up without binding
// because jQuery doesn't have the 'name' and 'age' properties.
this.talk = __bind(this.talk, this);
this.isAdult = __bind(this.isAdult, this);
}
// Define prototypes
Person.prototype.isAdult = function() {
// The value of this is guaranteed to point to the constructor
// function because of the binding done on the previous lines.
return this.age > 18;
};
Person.prototype.talk = function() {
return console.debug("Hello. My name is " + this.name + " and I'm " + this.age + " years old.");
};
// Return the Person constructor function. This is an example of
// how a closure works:
//
// Everything defined inside the anonymously invoked function
// is private / not accessible from the global scope. The Person variable
// above is simply being set to whatever this current anonymously invoked
// function returns. Therefore, if you omitted this line, Person would have
// the value of null. This would be the same as if everything defined inside
// this anonymously invoked function never existed. Instead, we create a
// FUNCTION that initializes some properties. We then add some methods
// to that FUNCTION's prototype. We then return that FUNCTION, which
// has the prototypes defined on it, which serves as a constructor
// function.
return Person;
})();
// Call the outer namespace function using the global scope (usually window, or
// exports in node). This means that the only things available to the global scope
// are the Person and __bind functions.
}).call(this);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment