Skip to content
PhoenixAndMachine edited this page Apr 7, 2014 · 5 revisions

Simply Class Style

In Javascript, there is no keyword class, so how we can define a class ? Here goes examples.

/* Anim class */
var Anim = function(name) {
    this.name = name;
};

/* declare methods */
Anim.prototype.start = function() {
    console.log("Anim object " + this.name + " starts");
};

Anim.prototype.stop = function() {
    console.log("Anim object " + this.name + " stops");
};

/* Usage */
var anim = new Anim("robot");

anim.start();
anim.stop();

It is how class is defined in Javascript. If it is Java, C++ or Python, you may have those little boxes in your head how class and object are located and connected with each other. So how do those boxes look like ? It is a piece of simple code. However, Javascript does a lot of work behind the scene. Let's chop this small piece of code into micro pieces.

Define a Class

/* Anim class */

var Anim = function(name) {
	this.name = name;
};

/* What is Anim */
console.log(Anim); 
/* 
	Output: [Function]
	It is an anonymous function
*/

/* So it is an instance of Function ? */
console.log(Anim instanceof Function);
/*
	Output: true
*/
/*
	How Js knows Anim is an instance of Function, Anim has some attribute called type or class?
	No, it doesn't, but it has something similar, called prototype chain, which is used to inherite
	class in Javascript
*/
console.log(Anim.__proto__===Function.prototype);
/*
	Output: true
	When variable Anim is assigned, JS provides it an attribute called __proto__, which is used to
	define its class. In this case, Anim is an instance of Function.
*/
/*
	So Anim is an instance of Function, well, what does it have from Function ?
*/
var AnimOwnProperties = Object.getOwnPropertyNames(Anim);
for(var i=0, length=AnimOwnProperties.length; i<length; i++) {
	console.log(AnimOwnProperties[i]+ " : " + Anim[AnimOwnProperties[i]]);
}
/*
	Output 
		caller : null
		length : 1
		prototype : [object Object]
		arguments : null
		name :
	Explain
		caller: the one which calls Anim
		length: the length of arguments defined in Anim
		prototype: the behavior of Anim's instances should have
		arguments: the arguments of Anim when it is called
		name: the name of function which defines Anim. In this case, anonymous, so empty
*/
/*
	What is in prototype ?
*/
var prototypeOfAnim = Object.getOwnPropertyNames(Anim.prototype);
for(var i=0, length=prototypeOfAnim.length; i<length; i++) {
	console.log(prototypeOfAnim[i]+ " : " + Anim[prototypeOfAnim[i]]);
}
/*
	Output
		constructor : function (name) {
			this.name = name;
		}
	WoW, So it is the definition of Anim ?
*/
console.log(Anim===Anim.prototype.constructor);
/*
	Output: true
	Yes, Anim.prototype.constructor is referred to Anim itself.
*/

Define methods in class

/* declare methods */
 
Anim.prototype.start = function() {
	console.log("Anim object " + this.name + " starts");
};

Anim.prototype.stop = function() {
	console.log("Anim object " + this.name + " stops");
};
/*
There are more stuff defined in Anim.prototype. Let's have a look.
*/
prototypeOfAnim = Object.getOwnPropertyNames(Anim.prototype);
for(var i=0, length=prototypeOfAnim.length; i<length; i++) {
	console.log(prototypeOfAnim[i]+ " : " + Anim.prototype[prototypeOfAnim[i]]);
}
/*
	Output
		constructor : function (name) {
			this.name = name;
		}
		stop : function () {
			console.log("Anim object " + this.name + " stops");
		}
		start : function () {
			console.log("Anim object " + this.name + " starts");
		}
*/

Generate instance and use its attributes and methods

/* Usage */

var robot = new Anim("robot");
var space = new Anim("space");
/*
	So robot and space are instances of Anim, I can prove it by
*/
console.log(robot.__proto__===Anim.prototype);
console.log(space.__proto__===Anim.prototype);
/*
	Output: 
		true
		true
	So they share the same class space, then they should just have one copy for those methods
	in class Anim.
*/
console.log(robot.__proto__===space.__proto__);
console.log(space.start===robot.start);
console.log(space.start===Anim.prototype.start);
/*
	Output:
		true
		true
		true
*/

/*
	Which more attributes or methods do space and robot have ?
*/
prototypeOfspace = Object.getOwnPropertyNames(space);
for(var i=0, length=prototypeOfspace.length; i<length; i++) {
	console.log(prototypeOfspace[i]+ " : " + space[prototypeOfspace[i]]);
}
/*
	Output
		name : space

*/
robot.start();
robot.stop();

From this example, we can conclue following points:

  1. Class is defined as a function, which is also its constructor. Let's name the class X.
  2. Attributes and Methods in Class is defined in X.prototype, including constructor method.
  3. instance_x = new X(constructor_arguments) => instance_x.__proto__ === X.prototype, which means, instance_x can call all the methods in X.prototype. That is a prototype chain inheritance in Javascript.
  4. The joke is there is NO Class in Javascript, only object instantiation. X, instance_x are actually objects. The prototype chain defines their relationship. Their own properties can be defined directly by
X.property_name = X_property_value

instance_x.property_name = instance_x_property_value
  1. When you call instance_x.something:
    • instance_x searches its own properties first, whether there exists something. You can check its own properties by Object.getOwnPropertyNames(instance_x);
    • instance_x searches its instance_x.__proto__ if something does not exist in the first step. instance_x.__proto__ is referred to instance_x's superclass, in this case, X.prototype;
    • If instance_x can not find the something, it will throw an error, otherwise execute it.

Composition of __proto__ and prototype

Let's be creative to have some fun with the method define in class. In the previous example, since we know the fact Anim.__proto__===Function.prototype, which means Anim can call any methods defined in Function.prototype. Then we define a method called method in Function.prototype in order to create methods in Anim.

/* Add a method to the Function object that can be used to declare methods */

Function.prototype.method = function(name, fn) {
    this.prototype[name] = fn;
}

/* Anim class */

var Anim = function(name) {
	this.name = name;
};

console.log(Object.getOwnPropertyNames(Anim.prototype)); // [ 'constructor' ]

Anim.method("start", function() {
        console.log("Anim object " + this.name + " starts");
    }
);

Anim.method("stop", function() {
	console.log("Anim object " + this.name + " stops");
    }
);

console.log(Object.getOwnPropertyNames(Anim.prototype)); // [ 'stop', 'constructor', 'start' ]

/* Usage */

var anim = new Anim("robot");

anim.start();
anim.stop();

Make a chain

Let's play a chain design pattern on this example a bit.( who doesn't like sequence ? :D Concurrency ? )

Function.prototype.method = function(name, fn) {
	this.prototype[name] = fn;
	return this; /* MAKE IT CHAIN */
};

/* Define Anim */

var Anim = function(name) {
    this.name = name;
};

Anim.
method('start', function() {
    console.log(this.name + " starts");
}).
method('stop', function() {
    console.log(this.name + ' stops');
});

var anim = new Anim("hubot");

anim.start();
anim.stop();