6. Classes

Classes1 are essentially a blueprint for creating new instances of an object with predefined functions and variables. These instances can then store state related to that individual instance. Over the years, JavaScript has constantly come under attack for its lack of any real support for classes.

In Chapter 5, “Collections and Iterations,” we looked at objects in JavaScript. In each example we hand rolled a brand new object and gave that new object a set of functions and values. This works great for the occasional simple object, but what if we wanted to have a more complex data model? More importantly, what if we wanted to have more than one of those complex data models? That is typically where classes come in.

Fortunately, CoffeeScript steps up to the plate with full class support. So if JavaScript does not have any real support for classes, how does CoffeeScript solve the problem? The short answer is through the use of some clever scoping and the use of functions and objects. The long answer is, read the rest of this chapter.

Defining Classes

Defining classes in CoffeeScript can be as simple and as straightforward as one line:

Example: (source: simple_class1.coffee)


class Employee


Example: (source: simple_class1.js)


(function() {
  var Employee;

  Employee = (function() {

    function Employee() {}

    return Employee;

  })();

}).call(this);


With that, we have defined a simple new class called Employee. We can instantiate new instances of that object like so:

Example: (source: simple_class2.coffee)


class Employee

emp1 = new Employee()
emp2 = new Employee()


Example: (source: simple_class2.js)


(function() {
  var Employee, emp1, emp2;

  Employee = (function() {

    function Employee() {}

    return Employee;

  })();

  emp1 = new Employee();

  emp2 = new Employee();

}).call(this);


We call the new keyword before the name of our class, and we get a brand-new instance of that object to do with as we like.


Tip

When creating new instances of objects with the new keyword, it isn’t required to put the parentheses at the end, as we do in our examples, but I find it looks nice and makes the code a bit easier to read. Do whatever feels right to you.


Defining Functions

When defining functions on our classes we follow the same rules, and syntax, that we would if we were defining a function on a simple object—because that is exactly what we are doing.

Example: (source: simple_class_with_function.coffee)


class Employee

  dob: (year = 1976, month = 7, day = 24)->
    new Date(year, month, day)

emp1 = new Employee()
console.log emp1.dob()
emp2 = new Employee()
console.log emp2.dob(1979, 3, 28)


Example: (source: simple_class_with_function.js)


(function() {
  var Employee, emp1, emp2;

  Employee = (function() {

    function Employee() {}

    Employee.prototype.dob = function(year, month, day) {
      if (year == null) year = 1976;
      if (month == null) month = 7;
      if (day == null) day = 24;
      return new Date(year, month, day);
    };

    return Employee;

  })();

  emp1 = new Employee();

  console.log(emp1.dob());

  emp2 = new Employee();

  console.log(emp2.dob(1979, 3, 28));

}).call(this);


Output: (source: simple_class_with_function.coffee)


Tue, 24 Aug 1976 04:00:00 GMT
Sat, 28 Apr 1979 05:00:00 GMT


The constructor Function

CoffeeScript lets us define a function called constructor that will be called when we create a new instance of an object. The constructor is like every other function we’ve talked about. The only thing special about the constructor function is that it will be called automatically when we instantiate a new instance of an object without us having to call it explicitly.


Tip

I told a little white lie earlier when I said that the constructor function gets called automatically when we create a new instance of an object. In reality, when we create a new instance of an object like this, new Employee(), we are calling that constructor function directly. It’s just renamed.


Example: (source: simple_class3.coffee)


class Employee

  constructor: ->
    console.log "Instantiated a new Employee object"

  dob: (year = 1976, month = 7, day = 24)->
    new Date(year, month, day)

emp1 = new Employee()
console.log emp1.dob()

emp2 = new Employee()
console.log emp2.dob(1979, 3, 28)


Example: (source: simple_class3.js)


(function() {
  var Employee, emp1, emp2;

  Employee = (function() {

    function Employee() {
      console.log("Instantiated a new Employee object");
    }

    Employee.prototype.dob = function(year, month, day) {
      if (year == null) year = 1976;
      if (month == null) month = 7;
      if (day == null) day = 24;
      return new Date(year, month, day);
    };

    return Employee;

  })();

  emp1 = new Employee();

  console.log(emp1.dob());

  emp2 = new Employee();

  console.log(emp2.dob(1979, 3, 28));

}).call(this);


Output: (source: simple_class3.coffee)


Instantiated a new Employee object
Tue, 24 Aug 1976 04:00:00 GMT
Instantiated a new Employee object
Sat, 28 Apr 1979 05:00:00 GMT


As you can see in our example, every time we create a new Employee object, it will print a message out to the console to let us know that it’s been created.

As we’ll see in this chapter, the constructor function can provide an easy way to quickly set up your new object with any custom data for that object.

Scope in Classes

At their heart, classes in CoffeeScript are just glorified objects that produce a lot of boilerplate JavaScript code to let them do the things they do. Because classes are just plain objects—granted, objects with a lot of window dressing—scope of variables, attributes, and functions behave the same as they do in regular objects.

Let’s investigate by taking our Employee class again. Employees in real life have names, so let’s make sure our Employee class can reflect it. When we create a new instance of the Employee class, we want to have the name passed in and assigned to an attribute that is scoped to the instance of the new Employee object.

Example: (source: class_scope.coffee)


class Employee

  constructor: (name)->
    @name = name

  dob: (year = 1976, month = 7, day = 24)->
    new Date(year, month, day)

emp1 = new Employee("Mark")
console.log emp1.name
console.log emp1.dob()

emp2 = new Employee("Rachel")
console.log emp2.name
console.log emp2.dob(1979, 3, 28)


Example: (source: class_scope.js)


(function() {
  var Employee, emp1, emp2;

  Employee = (function() {

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

    Employee.prototype.dob = function(year, month, day) {
      if (year == null) year = 1976;
      if (month == null) month = 7;
      if (day == null) day = 24;
      return new Date(year, month, day);
    };

    return Employee;

  })();

  emp1 = new Employee("Mark");

  console.log(emp1.name);

  console.log(emp1.dob());

  emp2 = new Employee("Rachel");

  console.log(emp2.name);

  console.log(emp2.dob(1979, 3, 28));

}).call(this);


Output: (source: class_scope.coffee)


Mark
Tue, 24 Aug 1976 04:00:00 GMT
Rachel
Sat, 28 Apr 1979 05:00:00 GMT


As we discussed earlier, the constructor function is no different from any other function, so the same scope and definition rules apply. Knowing that we are now passing in an argument called name, we set the name argument onto an attribute on the object instance using @name = name. Remember from Chapter 3, “Control Structures,” that the @ alias is equal to this.

With the attribute set on the object instance, we can then call it like any other attribute on the object.

There is an even easier and cleaner way to achieve the same goal, and it’s quite possibly one of my favorite features of CoffeeScript (and one I wish other languages would implement). We can trim our constructor down by doing the following:

Example: (source: class_scope1.coffee)


class Employee

  constructor: (@name)->

  dob: (year = 1976, month = 7, day = 24)->
    new Date(year, month, day)

emp1 = new Employee("Mark")
console.log emp1.name
console.log emp1.dob()

emp2 = new Employee("Rachel")
console.log emp2.name
console.log emp2.dob(1979, 3, 28)


Example: (source: class_scope1.js)


(function() {
  var Employee, emp1, emp2;

  Employee = (function() {

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

    Employee.prototype.dob = function(year, month, day) {
      if (year == null) year = 1976;
      if (month == null) month = 7;
      if (day == null) day = 24;
      return new Date(year, month, day);
    };

    return Employee;

  })();

  emp1 = new Employee("Mark");

  console.log(emp1.name);

  console.log(emp1.dob());

  emp2 = new Employee("Rachel");
  console.log(emp2.name);
  console.log(emp2.dob(1979, 3, 28));

}).call(this);


Output: (source: class_scope1.coffee)


Mark
Tue, 24 Aug 1976 04:00:00 GMT
Rachel
Sat, 28 Apr 1979 05:00:00 GMT


By adding the @ operator in front of the definition of the name argument, we tell CoffeeScript that we want it to generate JavaScript that will assign that argument to an attribute with the same name, in this case called name.

We can also easily access that attribute in the other functions of the class. Let’s update the example again, this time to add a method that prints out the employee’s name and birth date:

Example: (source: class_scope2.coffee)


class Employee

  constructor: (@name)->

  dob: (year = 1976, month = 7, day = 24)->
    new Date(year, month, day)

  printInfo: (year = 1976, month = 7, day = 24)->
    console.log "Name: #{@name}"
    console.log "DOB: #{@dob(year, month, day)}"


emp1 = new Employee("Mark")
emp1.printInfo(1976, 7, 24)

emp2 = new Employee("Rachel")
emp2.printInfo(1979, 3, 28)


Example: (source: class_scope2.js)


(function() {
  var Employee, emp1, emp2;

  Employee = (function() {

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

    Employee.prototype.dob = function(year, month, day) {
      if (year == null) year = 1976;
      if (month == null) month = 7;
      if (day == null) day = 24;
      return new Date(year, month, day);
    };

    Employee.prototype.printInfo = function(year, month, day) {
      if (year == null) year = 1976;
      if (month == null) month = 7;
      if (day == null) day = 24;
      console.log("Name: " + this.name);
      return console.log("DOB: " + (this.dob(year, month, day)));
    };

    return Employee;

  })();

  emp1 = new Employee("Mark");

  emp1.printInfo(1976, 7, 24);

  emp2 = new Employee("Rachel");

  emp2.printInfo(1979, 3, 28);

}).call(this);


Output: (source: class_scope2.coffee)


Name: Mark
DOB: Tue Aug 24 1976 00:00:00 GMT-0400 (EDT)
Name: Rachel
DOB: Sat Apr 28 1979 00:00:00 GMT-0500 (EST)


I would be remiss if we left this section with the code looking like that. I don’t want to have to keep passing in the year, month, and day every time I want to print out the employee’s information. I want to pass a birth date into the constructor function and then access it whenever I want. So let’s do that:

Example: (source: class_scope_refactor_1.coffee)


class Employee

  constructor: (@name, @dob)->

  printInfo: ->
    console.log "Name: #{@name}"
    console.log "DOB: #{@dob}"


emp1 = new Employee("Mark", new Date(1976, 7, 24))
emp1.printInfo()

emp2 = new Employee("Rachel", new Date(1979, 3, 28))
emp2.printInfo()


Example: (source: class_scope_refactor_1.js)


(function() {
  var Employee, emp1, emp2;

  Employee = (function() {
    function Employee(name, dob) {
      this.name = name;
      this.dob = dob;
    }

    Employee.prototype.printInfo = function() {
      console.log("Name: " + this.name);
      return console.log("DOB: " + this.dob);
    };

    return Employee;

  })();

  emp1 = new Employee("Mark", new Date(1976, 7, 24));

  emp1.printInfo();

  emp2 = new Employee("Rachel", new Date(1979, 3, 28));

  emp2.printInfo();

}).call(this);


Output: (source: class_scope_refactor_1.coffee)


Name: Mark
DOB: Tue Aug 24 1976 00:00:00 GMT-0400 (EDT)
Name: Rachel
DOB: Sat Apr 28 1979 00:00:00 GMT-0500 (EST)


That is certainly cleaner and a little more DRY.2 Therefore, when I see two arguments for a function I start asking the question, will there be more arguments? If the answer is yes, I reconsider how I’m defining my function; in this case I’m concerned about the constructor function. What happens when we need to pass in other information, such as salary, department, manager, and so on? Let’s do a bit more refactoring:

Example: (source: class_scope_refactor_2.coffee)


class Employee

  constructor: (@attributes)->

  printInfo: ->
    console.log "Name: #{@attributes.name}"
    console.log "DOB: #{@attributes.dob}"

    if @attributes.salary
      console.log "Salary: #{@attributes.salary}"
    else
      console.log "Salary: Unknown"

emp1 = new Employee
  name: "Mark"
  dob: new Date(1976, 7, 24)
  salary: "$1.00"

emp1.printInfo()

emp2 = new Employee
  name: "Rachel"
  dob: new Date(1979, 3, 28)

emp2.printInfo()


Example: (source: class_scope_refactor_2.js)


(function() {
  var Employee, emp1, emp2;

  Employee = (function() {

    function Employee(attributes) {
      this.attributes = attributes;
    }

    Employee.prototype.printInfo = function() {
      console.log("Name: " + this.attributes.name);
      console.log("DOB: " + this.attributes.dob);
      if (this.attributes.salary) {
        return console.log("Salary: " + this.attributes.salary);
      } else {
        return console.log("Salary: Unknown");
      }
    };

    return Employee;

  })();

  emp1 = new Employee({
    name: "Mark",
    dob: new Date(1976, 7, 24),
    salary: "$1.00"
  });

  emp1.printInfo();
  emp2 = new Employee({
    name: "Rachel",
    dob: new Date(1979, 3, 28)
  });

  emp2.printInfo();

}).call(this);


Output: (source: class_scope_refactor_2.coffee)


Name: Mark
DOB: Tue Aug 24 1976 00:00:00 GMT-0400 (EDT)
Salary: $1.00
Name: Rachel
DOB: Sat Apr 28 1979 00:00:00 GMT-0500 (EST)
Salary: Unknown


Now we can pass any arguments we want into the Employee class, and they’ll be available to us through the attributes attribute on the object. That is definitely a lot nicer, and it certainly is more extensible down the line.

As you can see with the first employee, we passed in a third attribute, salary, that we didn’t pass in for the second employee. We could pass in a hundred different attributes to our Employee class now and our code doesn’t have to change.

One last thing before we leave this section. Some of you might have come up with what you think of as a great idea. What if you use the knowledge you gained in Chapter 5, “Collections and Iterations,” about looping through the attributes argument and assigning each key/value pair directly to the object so you don’t have to keep calling @attributes everywhere in the code?

Well, let’s see how you would do that, and we can also see why that could be a very bad idea:

Example: (source: class_scope_refactor_3.coffee)


class Employee

  constructor: (@attributes)->
    for key, value of @attributes
      @[key] = value

  printInfo: ->
    console.log "Name: #{@name}"
    console.log "DOB: #{@dob}"
    if @salary
      console.log "Salary: #{@salary}"
    else
      console.log "Salary: Unknown"

emp1 = new Employee
  name: "Mark"
  dob: new Date(1976, 7, 24)
  salary: "$1.00"

emp1.printInfo()

emp2 = new Employee
  name: "Rachel",
  dob: new Date(1979, 3, 28)
  printInfo: ->
    console.log "I've hacked your code!"

emp2.printInfo()


Example: (source: class_scope_refactor_3.js)


(function() {
  var Employee, emp1, emp2;

  Employee = (function() {

    function Employee(attributes) {
      var key, value, _ref;
      this.attributes = attributes;
      _ref = this.attributes;
      for (key in _ref) {
        value = _ref[key];
        this[key] = value;
      }
    }

    Employee.prototype.printInfo = function() {
      console.log("Name: " + this.name);
      console.log("DOB: " + this.dob);
      if (this.salary) {
        return console.log("Salary: " + this.salary);
      } else {
        return console.log("Salary: Unknown");
      }
    };

    return Employee;

  })();

  emp1 = new Employee({
    name: "Mark",
    dob: new Date(1976, 7, 24),
    salary: "$1.00"
  });

  emp1.printInfo();

  emp2 = new Employee({
    name: "Rachel",
    dob: new Date(1979, 3, 28),
    printInfo: function() {
      return console.log("I've hacked your code!");
    }
  });

  emp2.printInfo();

}).call(this);


Output: (source: class_scope_refactor_3.coffee)


Name: Mark
DOB: Tue Aug 24 1976 00:00:00 GMT-0400 (EDT)
Salary: $1.00
I've hacked your code!


Uh-oh! We were easily able to override the printInfo function when we passed in the list of attributes for our second employee. The code is definitely easier to read, but it’s also easier to hack, and who wants that? JavaScript is easy enough to modify on its own, so why should we make it even easier? With that said, let’s move on, pretending we didn’t just do that last refactor.

Extending Classes

When writing object-oriented programs, developers often come across the need for inheritance.3 Inheritance lets us take a class, such as our Employee class, and create a variant on that class.

In a business, everyone is an employee, but not everybody is a manager. So let’s define a Manager class that inherits, or extends, from our Employee class:

Example: (source: manager1.coffee)


class Employee

  constructor: (@attributes)->

  printInfo: ->
    console.log "Name: #{@attributes.name}"
    console.log "DOB: #{@attributes.dob}"
    console.log "Salary: #{@attributes.salary}"

class Manager extends Employee

employee = new Employee
  name: "Mark"
  dob: new Date(1976, 7, 24)
  salary: 50000

employee.printInfo()

manager = new Manager
  name: "Rachel"
  dob: new Date(1979, 3, 28)
  salary: 100000

manager.printInfo()


Example: (source: manager1.js)


(function() {
  var Employee, Manager, employee, manager,
    __hasProp = Object.prototype.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; };

  Employee = (function() {
    function Employee(attributes) {
      this.attributes = attributes;
    }

    Employee.prototype.printInfo = function() {
      console.log("Name: " + this.attributes.name);
      console.log("DOB: " + this.attributes.dob);
      return console.log("Salary: " + this.attributes.salary);
    };

    return Employee;

  })();

  Manager = (function(_super) {

    __extends(Manager, _super);

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

    return Manager;

  })(Employee);

  employee = new Employee({
    name: "Mark",
    dob: new Date(1976, 7, 24),
    salary: 50000
  });

  employee.printInfo();

  manager = new Manager({
    name: "Rachel",
    dob: new Date(1979, 3, 28),
    salary: 100000
  });

  manager.printInfo();

}).call(this);


Output: (source: manager1.coffee)


Name: Mark
DOB: Tue Aug 24 1976 00:00:00 GMT-0400 (EDT)
Salary: 50000
Name: Rachel
DOB: Sat Apr 28 1979 00:00:00 GMT-0500 (EST)
Salary: 100000



Tip

In the real world we would probably use roles to define different types of employees, but for the sake of discussion let’s pretend that this is the best way to solve our problem of different types of employees.


The basic definition of our Manager class was simple, class Manager, but by using the keyword extends and giving it the name of the class we wanted to extend, Employee, we were able to gain all the functionality from the Employee class in our Manager class.

Let’s take it a step further and learn about how to override methods in subclasses. Let’s add a function, bonus, to our Employee class that returns 0. Regular employees apparently don’t get bonuses. Managers, however, get a 10% bonus, so we want to make sure when we call the bonus function on managers we get the right value.

Example: (source: manager2.coffee)


class Employee

  constructor: (@attributes)->

  printInfo: ->
    console.log "Name: #{@attributes.name}"
    console.log "DOB: #{@attributes.dob}"
    console.log "Salary: #{@attributes.salary}"
    console.log "Bonus: #{@bonus()}"

  bonus: ->
    0

class Manager extends Employee

  bonus: ->
    @attributes.salary * .10

employee = new Employee
  name: "Mark"
  dob: new Date(1976, 7, 24)
  salary: 50000

employee.printInfo()

manager = new Manager
  name: "Rachel"
  dob: new Date(1979, 3, 28)
  salary: 100000

manager.printInfo()


Example: (source: manager2.js)


(function() {
  var Employee, Manager, employee, manager,
    __hasProp = Object.prototype.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; };

  Employee = (function() {

    function Employee(attributes) {
      this.attributes = attributes;
    }

    Employee.prototype.printInfo = function() {
      console.log("Name: " + this.attributes.name);
      console.log("DOB: " + this.attributes.dob);
      console.log("Salary: " + this.attributes.salary);
      return console.log("Bonus: " + (this.bonus()));
    };

    Employee.prototype.bonus = function() {
      return 0;
    };

    return Employee;

  })();

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

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

    Manager.prototype.bonus = function() {
      return this.attributes.salary * .10;
    };

    return Manager;

  })(Employee);
  employee = new Employee({
    name: "Mark",
    dob: new Date(1976, 7, 24),
    salary: 50000
  });

  employee.printInfo();

  manager = new Manager({
    name: "Rachel",
    dob: new Date(1979, 3, 28),
    salary: 100000
  });

  manager.printInfo();

}).call(this);


Output: (source: manager2.coffee)


Name: Mark
DOB: Tue Aug 24 1976 00:00:00 GMT-0400 (EDT)
Salary: 50000
Bonus: 0
Name: Rachel
DOB: Sat Apr 28 1979 00:00:00 GMT-0500 (EST)
Salary: 100000
Bonus: 10000


Overriding functions on the subclass is as simple as redefining the function again with new functionality. But what about if we want to call the original function and maybe add a little bit to it. Here’s an example.

Employees get really upset when they see their bonus amount as 0 in the printInfo, so we’re going to take it out entirely, but we still want managers to see that information when we call printInfo. We can do this using the super keyword:

Example: (source: manager3.coffee)


class Employee

  constructor: (@attributes)->

  printInfo: ->
    console.log "Name: #{@attributes.name}"
    console.log "DOB: #{@attributes.dob}"
    console.log "Salary: #{@attributes.salary}"

  bonus: ->
    0

class Manager extends Employee

  bonus: ->
    @attributes.salary * .10

  printInfo: ->
    super
    console.log "Bonus: #{@bonus()}"

employee = new Employee
  name: "Mark"
  dob: new Date(1976, 7, 24)
  salary: 50000

employee.printInfo()

manager = new Manager
  name: "Rachel"
  dob: new Date(1979, 3, 28)
  salary: 100000

manager.printInfo()


Example: (source: manager3.js)


(function() {
  var Employee, Manager, employee, manager,
    __hasProp = Object.prototype.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; };

  Employee = (function() {

    function Employee(attributes) {
      this.attributes = attributes;
    }

    Employee.prototype.printInfo = function() {
      console.log("Name: " + this.attributes.name);
      console.log("DOB: " + this.attributes.dob);
      return console.log("Salary: " + this.attributes.salary);
    };
    Employee.prototype.bonus = function() {
      return 0;
    };

    return Employee;

  })();

  Manager = (function(_super) {

    __extends(Manager, _super);

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

    Manager.prototype.bonus = function() {
      return this.attributes.salary * .10;
    };

    Manager.prototype.printInfo = function() {
      Manager.__super__.printInfo.apply(this, arguments);
      return console.log("Bonus: " + (this.bonus()));
    };

    return Manager;

  })(Employee);

  employee = new Employee({
    name: "Mark",
    dob: new Date(1976, 7, 24),
    salary: 50000
  });

  employee.printInfo();

  manager = new Manager({
    name: "Rachel",
    dob: new Date(1979, 3, 28),
    salary: 100000
  });

  manager.printInfo();

}).call(this);


Output: (source: manager3.coffee)


Name: Mark
DOB: Tue Aug 24 1976 00:00:00 GMT-0400 (EDT)
Salary: 50000
Name: Rachel
DOB: Sat Apr 28 1979 00:00:00 GMT-0500 (EST)
Salary: 100000
Bonus: 10000


In the printInfo function we defined in the Manager class, we first called super. When we call super, that will call the original printInfo function from the Employee class with any arguments that might have come into the function. After we call super we then print out the bonus information for the manager.


Tip

The call to super can be called at any point in the function that is overriding it; it doesn’t have to be first (as in some languages). Subsequently, you don’t have to call the super method at all.



Tip

When calling super we do not need to explicitly pass in arguments. By default, any arguments that come into the function that is overriding super will be passed to super when called. You can, however, explicitly pass any arguments you want when calling super. This can be useful if you want to augment or mutate the arguments before they go to the super method.


Class-Level Functions

Class-level functions are functions that don’t require an instance of the class in order to be called. These functions can be incredibly useful. One of the best uses is to provide a sort of namespacing for your functions. An example of this in JavaScript is Math.random(). You don’t need to instantiate a new Math object to get a random number. But by hanging the random function off of the Math class, you avoid the risk that there is another function called random that might override your function.


Tip

In reality, Math isn’t a class; it’s just a plain object that is used for namespacing. Occasionally I twist the truth a little bit to help get my point across.


You can also use class-level functions to do things that might affect multiple instances of a class, like search for them in a database.

We could write a couple of class-level functions on the Employee class. Since we don’t have a full database at our disposal, we’ll just keep track of how many employee instances we create and report that number. To keep track of which employees we hire, we create a class-level function called hire that will take the newly hired employees and add them to an array that will act as our impromptu database. We’ll also add a total class-level function that will return the total number of employees we have in our faux database.

Example: (source: class_level.coffee)


class Employee

  constructor: ->
    Employee.hire(@)

  @hire: (employee) ->
    @allEmployees ||= []
    @allEmployees.push employee

  @total: ->
    console.log "There are #{@allEmployees.length} employees."
    @allEmployees.length

new Employee()
new Employee()
new Employee()

Employee.total()


Example: (source: class_level.js)


(function() {
  var Employee;

  Employee = (function() {

    function Employee() {
      Employee.hire(this);
    }

    Employee.hire = function(employee) {
      this.allEmployees || (this.allEmployees = []);
      return this.allEmployees.push(employee);
    };

    Employee.total = function() {
      console.log("There are " + this.allEmployees.length + " employees.");

      return this.allEmployees.length;
    };

    return Employee;

  })();

  new Employee();

  new Employee();

  new Employee();

  Employee.total();

}).call(this);


Output: (source: class_level.coffee)


There are 3 employees.


So how did we create those class-level functions? By prepending the function name with @ we are telling CoffeeScript that we want that function to be at the class level. In the JavaScript world this works because when we replace @ with this. the this context is that of the Employee class, not an instance of that class.

The inside class-level functions scope is limited to other class-level functions and attributes.


Tip

I routinely create class definitions that are nothing but a bunch of class-level methods. This is great for building utility packages and to make sure that I have nicely scoped functions. And, if I ever want to, or need to, I can inherit from these classes and override certain functions for a particular need.


The use of super is limited, however, when dealing with class-level functions and attributes. You can use super when you want to call the original function that you have overridden in the subclass. However, and this is big, if the function you are calling using super tries to make reference to any class-level attributes, you’ll get a big fat error.

Let’s take a look at what happens when we override the total class-level function in the Manager class and then try to call super:

Example: (source: class_level_super.coffee)


class Employee

  constructor: ->
    Employee.hire(@)

  @hire: (employee) ->
    @allEmployees ||= []
    @allEmployees.push employee

  @total: ->
    console.log "There are #{@allEmployees.length} employees."
    @allEmployees.length

class Manager extends Employee

  @total: ->
    console.log "There are 0 managers."
    super

new Employee()
new Employee()
new Employee()

Manager.total()


Example: (source: class_level_super.js)


(function() {
  var Employee, Manager,
    __hasProp = Object.prototype.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; };

  Employee = (function() {

    function Employee() {
      Employee.hire(this);
    }

    Employee.hire = function(employee) {
      this.allEmployees || (this.allEmployees = []);
      return this.allEmployees.push(employee);
    };
    Employee.total = function() {
      console.log("There are " + this.allEmployees.length + " employees.");
      return this.allEmployees.length;
    };

    return Employee;

  })();

  Manager = (function(_super) {

    __extends(Manager, _super);

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

    Manager.total = function() {
      console.log("There are 0 managers.");
      return Manager.__super__.constructor.total.apply(this, arguments);
    };

    return Manager;

  })(Employee);

  new Employee();

  new Employee();

  new Employee();

  Manager.total();

}).call(this);


Output: (source: class_level_super.coffee)


There are 0 managers.
TypeError: Cannot read property 'length' of undefined
    at Function.<anonymous> (.../classes/class_level_super.coffee:18:51)
    at Function.total (.../classes/class_level_super.coffee:36:50)
    at Object.<anonymous> (.../classes/class_level_super.coffee:49:11)
    at Object.<anonymous> (.../classes/class_level_super.coffee:51:4)
    at Module._compile (module.js:432:26)
    at Object.run (/usr/local/lib/node_modules/coffee-script/lib/coffee-script/coffee-script.js:68:25)
    at /usr/local/lib/node_modules/coffee-script/lib/coffee-script/command.js:135:29
    at /usr/local/lib/node_modules/coffee-script/lib/coffee-script/command.js:110:18
    at [object Object].<anonymous> (fs.js:114:5)
    at [object Object].emit (events.js:64:17)


As you can see, when we tried to call length on the @allEmployees attribute we got the following error:

TypeError: Cannot read property 'length' of undefined

The reason for this is straightforward, but it might take a minute for it to sink in. Because JavaScript doesn’t have true inheritance, CoffeeScript has to cheat and do some magic, as we talked about earlier, to give the illusion of classes and inheritance. Because of this, the subclass Manager is a different object from the Employee class, and because the attributes are being set on the Employee class and not the Manager class, the Manager class doesn’t have access to them. I told you it was a bit to wrap your head around.


Tip

I find it best to try to avoid using super at the class level. I also try to keep all my class-level functions self-contained so I don’t run into these sorts of issues.


Prototype Functions

In JavaScript, you can add functions and attributes to all instances of an object by adding those functions and attributes to the object’s prototype using the aptly named prototype attribute.

In CoffeeScript we can do this using the :: operator. Let’s add a size function to all instances of array. We want the size function to return the length of the array.

Example: (source: prototypes.coffee)


myArray = [1..10]

try
  console.log myArray.size()
catch error
  console.log error

Array::size = -> @length
console.log myArray.size()

myArray.push(11)
console.log myArray.size()


Example: (source: prototypes.js)


(function() {
  var myArray;

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

  try {
    console.log(myArray.size());
  } catch (error) {
    console.log(error);
  }

  Array.prototype.size = function() {
    return this.length;
  };

  console.log(myArray.size());

  myArray.push(11);

  console.log(myArray.size());

}).call(this);


Output: (source: prototypes.coffee)


[TypeError: Object 1,2,3,4,5,6,7,8,9,10 has no method 'size']
10
11


The first time we try to call the size function on our array we get an error because the function doesn’t exist. However, after we add the size function to the Array class’s prototype, it behaves just as we hoped it would the next couple of times we call it.


Tip

The :: operator is there for convenience. You can still access the prototype attribute directly, but I find that typing all those extra characters isn’t worth it.


Binding (-> Versus =>)

JavaScript is an asynchronous4 or “evented” programming language. In non-asynchronous programming, each time a function is called execution of the rest of the program would be halted until the aforementioned function has returned. In JavaScript, this is not necessarily the case. It is quite common for the program to continue executing after calling a function, even though the function has yet to return. If it helps, think of this style of programming as “fire and forget;” the program fires off a call to a function and then forgets all about it. Let’s look at a quick example of how an asynchronous program runs.

Example: (source: fire_and_forget.coffee)


fire = (msg, wait)->
  setTimeout ->
    console.log msg
  , wait

fire("Hello", 3000)
fire("World", 1000)


Example: (source: fire_and_forget.js)


(function() {
  var fire;

  fire = function(msg, wait) {
    return setTimeout(function() {
      return console.log(msg);
    }, wait);
  };

  fire("Hello", 3000);

  fire("World", 1000);

}).call(this);


Output: (source: fire_and_forget.coffee)


World
Hello


As you can see, our program first said “World” and then “Hello.” In non-asynchronous programming we would have first seen the word “Hello” followed a few seconds later by the word “World.” Asynchronous programming can be incredibly powerful but can also be a bit cumbersome and confusing. Let’s look at how things can start to get a bit muddled up.

Let’s write a log method. This method will log to the console that we are about to execute a callback function; then it will execute the callback; and finally, it will log to the console that we have executed the function.

Example: (source: unbound.coffee)


class User

  constructor: (@name) ->

  sayHi: ->
    console.log "Hello #{@name}"

bob = new User('bob')
mary = new User('mary')

log = (callback)->

  console.log "about to execute callback..."
  callback()
  console.log "...executed callback"

log(bob.sayHi)
log(mary.sayHi)


Example: (source: unbound.js)


(function() {
  var User, bob, log, mary;

  User = (function() {

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

    User.prototype.sayHi = function() {
      return console.log("Hello " + this.name);
    };

    return User;

  })();

  bob = new User('bob'),

  mary = new User('mary'),

  log = function(callback) {
    console.log("about to execute callback...");
    callback();
    return console.log("...executed callback");
  };

  log(bob.sayHi);

  log(mary.sayHi);

}).call(this);


Output: (source: unbound.coffee)


about to execute callback...
Hello undefined
...executed callback
about to execute callback...
Hello undefined
...executed callback


Well, I’m pretty sure that code was not supposed to say hello to undefined. So what happened there? When the log function called the callback function we passed in, that callback had lost the original context from which it came. The callback no longer had reference to the name variable we set in our class. This type of problem is quite common in JavaScript, particularly when using libraries like jQuery when making AJAX requests or binding to events.

So how do we fix this? How do we give our callback back its context? The answer is to use =>, also known as the fat arrow, instead of -> when defining our sayHi function in the User class. Here is the same example, only this time I changed sayHi: -> to sayHi: =>. Let’s see what happens:

Example: (source: bound.coffee)


class User

  constructor: (@name) ->

  sayHi: =>
    console.log "Hello #{@name}"

bob = new User('bob')
mary = new User('mary')

log = (callback)->
  console.log "about to execute callback..."
  callback()
  console.log "...executed callback"

log(bob.sayHi)
log(mary.sayHi)


Example: (source: bound.js)


(function() {
  var User, bob, log, mary,
    __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };

  User = (function() {

    function User(name) {
      this.name = name;
      this.sayHi = __bind(this.sayHi, this);
    }

    User.prototype.sayHi = function() {
      return console.log("Hello " + this.name);
    };

    return User;

  })();

  bob = new User('bob'),

  mary = new User('mary'),

  log = function(callback) {
    console.log("about to execute callback...");
    callback();
    return console.log("...executed callback");
  };

  log(bob.sayHi);

  log(mary.sayHi);

}).call(this);


Output: (source: bound.coffee)


about to execute callback...
Hello bob
...executed callback
about to execute callback...
Hello mary
...executed callback


Amazing! One simple character did so much there. Let’s compare the JavaScript output of our unbound and bound examples to better understand what that one character did to our code:

Example: (source: unbound.js)


(function() {
  var User, bob, log, mary;

  User = (function() {

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

    User.prototype.sayHi = function() {
      return console.log("Hello " + this.name);
    };

    return User;

  })();

  bob = new User('bob'),

  mary = new User('mary'),

  log = function(callback) {
    console.log("about to execute callback...");
    callback();
    return console.log("...executed callback");
  };

  log(bob.sayHi);

  log(mary.sayHi);
}).call(this);


Example: (source: bound.js)


(function() {
  var User, bob, log, mary,
    __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };

  User = (function() {

    function User(name) {

      this.name = name;
      this.sayHi = __bind(this.sayHi, this);
    }

    User.prototype.sayHi = function() {
      return console.log("Hello " + this.name);
    };

    return User;

  })();

  bob = new User('bob'),

  mary = new User('mary'),

  log = function(callback) {
    console.log("about to execute callback...");
    callback();
    return console.log("...executed callback");
  };

  log(bob.sayHi);

  log(mary.sayHi);

}).call(this);


I will explain some of what is going on here, but if you don’t understand what the apply function does, I suggest reading up on it in a JavaScript book. There are two differences between the two JavaScript files. The first is the existence of the __bind function found in our => example. This function accepts two parameters, the first being the function that you would like to bind, and the second being the context to which you would like to bind the function. The __bind will return a new function that will call the original function using apply and passing in the context you provided.

The next difference is in the constructor for the User class. We are redefining the sayHi function by calling __bind and passing in the original definition of sayHi and the context of the class instance we are in.

If all that just confused you, don’t worry—you are not alone. Context and binding are very confusing subjects in JavaScript. If you don’t understand it, I recommend two things: First, get a good JavaScript book, and second, read “Understanding JavaScript Function Invocation and ‘this.’”5 Yehuda does a great job explaining it all in a couple-page blog post.

If you do understand the JavaScript that is going on here, you are probably smiling right now, knowing that you don’t have to deal with troublesome binding anymore. You can use => and your life will instantly be better. You’ll see this in action in Chapter 11, “Example: Todo List Part 2 (Client-side w/ jQuery).”

Wrapping Up

Well, wasn’t that fun? Classes in CoffeeScript are, for me at least, one of the biggest selling points of the language. I hope this chapter helped to sell you on them.

We covered a lot in this chapter. We looked at what a class is in CoffeeScript and how to define the most basic class possible.

Next, we looked at the “special” constructor function and talked a lot about scope in our classes.

We talked about how to extend classes in CoffeeScript using the extends keyword. We also learned how super can help us extend the original functionality of a super class’s function in a subclass.

After that, we talked in depth about class-level functions and prototype functions. We also learned about the trouble super can cause at the class level if we’re not careful.

We ended the chapter by looking at the rather complex, but insanely powerful, concept of bound functions using =>, also known as the fat arrow.

At this point you have learned pretty much all there is to learn about CoffeeScript. Congratulations! You are ready to leave the temple. This ends Part I of this book, “Core CoffeeScript.” Part II, “CoffeeScript in Practice,” will take all that we’ve learned from Part I and put it into practice using a few popular JavaScript libraries.

Notes

1. http://en.wikipedia.org/wiki/Class_(computer_programming)

2. http://en.wikipedia.org/wiki/DRY

3. http://en.wikipedia.org/wiki/Inheritance_(computer_science)

4. http://en.wikipedia.org/wiki/Asynchronous_I/O

5. http://yehudakatz.com/2011/08/11/understanding-javascript-function-invocation-and-this/

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.35.194