It's Alive!!! Building Objects in JavaScript
It's overwhelming the number of ways you can construct objects in JavaScript. In this article I will tackle the various ways to build objects (including inheritance).
The basic techniques for creating objects are:
- Simple Objects
- Constructors
- Simple Inheritance
- Inheritance method used by CoffeeScript
- Inheritance method used by TypeScript
Object Instantiation
var emp = {};
emp.hireDate = new Date(2010, 0, 10); // hired Jan. 10th 2010
emp.lastDay = null;
emp.isActive = function(){
var today = new Date();
if(this.hireDate < today &&
(this.lastDay === null || this.lastDay > today)) {
return true;
}
return false;
};
Constructor Function
function Employee() {
this.hireDate = new Date(2010, 0, 10); // hired Jan. 10th 2010
this.lastDay = null;
this.isActive = function(){
var today = new Date();
if(this.hireDate < today &&
(this.lastDay === null || this.lastDay > today)) {
return true;
}
return false;
};
}
// You can create instances of this employee using the new operator
// and invoking the Employee function as a constructor
var emp = new Employee();
Simple Inheritance
Now we want our Employee to inherit from a Person object. Every Employee will have a name so doing classical inheritance we would put the name on the "base class" or in this case the "base" constructor function. The gist of this technique is using Subclass.prototype = new SuperClass();
.
function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
Person.prototype.fullName = function() { return this.firstName + ' ' + this.lastName };
var person = new Person('Kyle', 'Nunery');
function Employee() {
this.hireDate = new Date(2010, 0, 10); // hired Jan. 10th 2010
this.lastDay = null;
this.isActive = function(){
var today = new Date();
if(this.hireDate < today &&
(this.lastDay === null || this.lastDay > today)) {
return true;
}
return false;
};
}
// Here is one way to make Employee inherit from Person
Employee.prototype = new Person('Kyle', 'Nunery');
var emp = new Employee();
There are downsides to this approach. First have to create an instance of a Person everytime we want to create an Employee. Secondly, we would want to augment the Employee constructor so we could pass in the firstName and lastName arguments.
Inheritance in CoffeeScript
CoffeeScript is a popular scripting language that compiles to plain ol JavaScript. Let's convert our Employee and Person code to CoffeeScript. You can play with this code using CoffeScript at this site http://coffeescript.org/ (click the 'Try CoffeeScript' tab).
class Person
constructor: (@firstName, @lastName) ->
fullName: () ->
@firstName + ' ' + @lastName
class Employee extends Person
@hireDate: new Date()
@lastDay: null
isActive: () ->
today = new Date()
@hireDate < today and (@lastDay is null or @lastDay > today)
emp = new Employee('Kyle', 'Nunery')
alert emp.fullName()
Let's examine the JavaScript that is created by the CoffeeScript compiler.
var Employee, Person, emp,
__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; };
Person = (function() {
function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
Person.prototype.fullName = function() {
return this.firstName + ' ' + this.lastName;
};
return Person;
})();
Employee = (function(_super) {
__extends(Employee, _super);
function Employee() {
return Employee.__super__.constructor.apply(this, arguments);
}
Employee.hireDate = new Date();
Employee.lastDay = null;
Employee.prototype.isActive = function() {
var today;
today = new Date();
return this.hireDate < today && (this.lastDay === null || this.lastDay > today);
};
return Employee;
})(Person);
emp = new Employee('Kyle', 'Nunery');
alert(emp.fullName());
The inheritance magic occurs in the __extends
function.
__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;
};
Inheritance in TypeScript
Here is the program converted to TypeScript. You can play with TypeScript at the following site http://www.typescriptlang.org/Playground/.
class Person {
constructor(public firstName: string, public lastName: string) { }
fullName() {
return this.firstName + ' ' + this.lastName
}
}
class Employee extends Person {
constructor(firstName: string, lastName: string) {
this.hireDate = new Date(2010, 0, 10);
this.lastDay = null;
super(firstName, lastName);
}
hireDate;
lastDay;
isActive() {
var today = new Date();
return this.hireDate < today &&
(this.lastDay === null || this.lastDay > today);
}
}
var emp = new Employee('Kyle', 'Nunery');
alert(emp.fullName());
TypeScript and CoffeeScript aren't too far off in their syntax. TypeScript allows you to give more compiler hints about types and is a little more verbose in places. Let's look at the compiled JavaScript produced by the TypeScript compiler.
var __extends = this.__extends || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
__.prototype = b.prototype;
d.prototype = new __();
};
var Person = (function () {
function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
Person.prototype.fullName = function () {
return this.firstName + ' ' + this.lastName;
};
return Person;
})();
var Employee = (function (_super) {
__extends(Employee, _super);
function Employee(firstName, lastName) {
this.hireDate = new Date(2010, 0, 10);
this.lastDay = null;
_super.call(this, firstName, lastName);
}
Employee.prototype.isActive = function () {
var today = new Date();
return this.hireDate < today && (this.lastDay === null || this.lastDay > today);
};
return Employee;
})(Person);
var emp = new Employee('Kyle', 'Nunery');
alert(emp.fullName());
The interesting part is the __extends
function.
var __extends = this.__extends || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
__.prototype = b.prototype;
d.prototype = new __();
};
The __extends
function in TypeScript works almost identically to the __extends
function in CoffeeScript.