Learning Javascript with Object Graphs (Part II)
The first article using graphs to describe JavaScript semantics was so popular that I've decided to try the technique with some more advanced ideas. In this article I'll explain three common techniques for creating objects. They are constructor with prototype, pure prototypal, and object factory.
My goal is that this will help people understand the strengths and weaknesses of each technique and understand what's really going on.
Classical JavaScript Constructors
function Rectangle(width, height) {
this.width = width;
this.height = height;
}
Rectangle.prototype.getArea = function getArea() {
return this.width * this.height;
};
Rectangle.prototype.getPerimeter = function getPerimeter() {
return 2 * (this.width + this.height);
};
Rectangle.prototype.toString = function toString() {
return this.constructor.name + " a=" + this.getArea() + " p=" + this.getPerimeter();
};function Square(side) {
this.width = side;
this.height = side;
}
Square.prototype.__proto__ = Rectangle.prototype;
Square.prototype.getPerimeter = function getPerimeter() {
return this.width * 4;
};var rect = new Rectangle(6, 4);
var sqr = new Square(5);
console.log(rect.toString())
console.log(sqr.toString())- Output
- Rectangle a=24 p=20 Square a=25 p=20
Pure Prototypal Objects
var Rectangle = {
name: "Rectangle",
getArea: function getArea() {
return this.width * this.height;
},
getPerimeter: function getPerimeter() {
return 2 * (this.width + this.height);
},
toString: function toString() {
return this.name + " a=" + this.getArea() + " p=" + this.getPerimeter();
}
};var Square = {
name: "Square",
getArea: function getArea() {
return this.width * this.width;
},
getPerimeter: function getPerimeter() {
return this.width * 4;
},
};
Square.__proto__ = Rectangle;var rect = Object.create(Rectangle);
rect.width = 6;
rect.height = 4;
var square = Object.create(Square);
square.width = 5;
console.log(rect.toString());
console.log(square.toString());- Output
- Rectangle a=24 p=20 Square a=25 p=20
Object Factories
function Controller(model, view) {
view.update(model.value);
return {
up: function onUp(evt) {
model.value++;
view.update(model.value);
},
down: function onDown(evt) {
model.value--;
view.update(model.value);
},
save: function onSave(evt) {
model.save();
view.close();
}
};
}var on = Controller(
// Inline a mock model
{
value: 5,
save: function save() {
console.log("Saving value " + this.value + " somewhere");
}
},
// Inline a mock view
{
update: function update(newValue) {
console.log("View now has " + newValue);
},
close: function close() {
console.log("Now hiding view");
}
}
);
setTimeout(on.up, 100);
setTimeout(on.down, 200);
setTimeout(on.save, 300);// Output
View now has 5
View now has 6
View now has 5
Saving value 5 somewhere
Now hiding view
Conclusion
There is so much more I want to explore, but I like to keep these articles somewhat short and bite-size. If there is demand, I'll write a part three explaining how to do ruby-style mixins and other advanced topics.
View the discussion thread.blog comments powered byDisqus
