Skip to content

Commit

Permalink
Write code and diagrams for object-graphs-2.
Browse files Browse the repository at this point in the history
  • Loading branch information
creationix committed Oct 9, 2010
1 parent 70a4301 commit bf25e9d
Show file tree
Hide file tree
Showing 7 changed files with 286 additions and 0 deletions.
54 changes: 54 additions & 0 deletions articles/object-graphs-2.markdown
@@ -0,0 +1,54 @@
Title: Learning Javascript with Object Graphs (Part II)
Author: Tim Caswell
Date: Wed Oct 06 2010 09:31:21 GMT-0700 (PDT)
Node: v0.2.3

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

<object-graphs-2/classical.js#rectangle>

<object-graphs-2/classical.js#square>

<object-graphs-2/classical.js#test*>

![classical](object-graphs-2/classical.dot)

<br style="clear:left"/>

## Pure Prototypal Objects

<object-graphs-2/prototypal.js#rectangle>

<object-graphs-2/prototypal.js#square>

<object-graphs-2/prototypal.js#test*>

![classical](object-graphs-2/prototypal.dot)

<br style="clear:left"/>

## Object Factories

<object-graphs-2/factory.js#controller>

<object-graphs-2/factory.js#usage>

// Output
View now has 5
View now has 6
View now has 5
Saving value 5 somewhere
Now hiding view


![classical](object-graphs-2/factory.dot)

<br style="clear:left"/>

## 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.
45 changes: 45 additions & 0 deletions articles/object-graphs-2/classical.dot
@@ -0,0 +1,45 @@
digraph finite_state_machine {
size="8.3,8"
rankdir = "LR"

/* Execution Contexts */
node [shape=Mrecord, fillcolor=beige, style=filled];
top [label="<__proto__>[ Scope ]|<Rectangle>Rectangle|<rect>rect|<Square>Square|<sqr>sqr"];

/* Normal Objects */
node [shape = Mrecord, fillcolor=lightskyblue, style=filled];
ob_RectProto [label = "<__proto__>[ Object ]|<constructor>constructor|<getArea>getArea|<getPerimeter>getPerimeter|<toString>toString"];
ob_SquareProto [label = "<__proto__>[ Object ]|<constructor>constructor|<getPerimeter>getPerimeter"];
ob_rect [label = "<__proto__>[ Object ]|<width>width=6|<height>height=4"];
ob_sqr [label = "<__proto__>[ Object ]|<width>width=5|<height>height=5"];

/* Function Objects */
node [shape = Mrecord, fillcolor=orange, style=filled];
fn_Rectangle [label="<__proto__>[Rectangle(width, height)]|\{this.width = width…\}|<prototype>prototype"];
fn_Square [label="<__proto__>[Square(width)]|\{this.width = width…\}|<prototype>prototype"];
fn_getArea [label="<__proto__>[getArea()]|\{return this.width * this.height\}"];
fn_getPerimeter [label="<__proto__>[getPerimeter()]|\{return 2 * (this.width + this.height)\}"];
fn_toString [label="<__proto__>[toString()]|\{return this.constructor.name…\}"];
fn_getPerimeter2 [label="<__proto__>[getPerimeter()]|\{return 4 * this.width\}"];

/* References */
top:Rectangle -> fn_Rectangle:__proto__;
top:Square -> fn_Square:__proto__;
top:rect -> ob_rect:__proto__;
top:sqr -> ob_sqr:__proto__;
fn_Rectangle:prototype -> ob_RectProto:__proto__;
fn_Square:prototype -> ob_SquareProto:__proto__;
ob_RectProto:constructor -> fn_Rectangle:__proto__;
ob_RectProto:getArea -> fn_getArea:__proto__;
ob_RectProto:getPerimeter -> fn_getPerimeter:__proto__;
ob_RectProto:toString -> fn_toString:__proto__;
ob_SquareProto:constructor -> fn_Square:__proto__;
ob_SquareProto:getPerimeter -> fn_getPerimeter2:__proto__;

/* Inheritance Chains */
edge [style=dashed]
ob_rect:__proto__ -> ob_RectProto:__proto__;
ob_sqr:__proto__ -> ob_SquareProto:__proto__;
ob_SquareProto:__proto__ -> ob_RectProto:__proto__;

}
28 changes: 28 additions & 0 deletions articles/object-graphs-2/classical.js
@@ -0,0 +1,28 @@
//rectangle
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();
};
//square
function Square(side) {
this.width = side;
this.height = side;
}
Square.prototype.__proto__ = Rectangle.prototype;
Square.prototype.getPerimeter = function getPerimeter() {
return this.width * 4;
};
//test
var rect = new Rectangle(6, 4);
var sqr = new Square(5);
console.log(rect.toString())
console.log(sqr.toString())
44 changes: 44 additions & 0 deletions articles/object-graphs-2/factory.dot
@@ -0,0 +1,44 @@
digraph finite_state_machine {
size="8.3,8"
rankdir = "LR"

/* Execution Contexts */
node [shape=Mrecord, fillcolor=beige, style=filled];
top [label="<__proto__>[ Scope ]|<Controller>Controller|<on>on"];
closure [label="<__proto__>[ Scope ]|<view>view|<model>model"];

/* Normal Objects */
node [shape = Mrecord, fillcolor=lightskyblue, style=filled];
ob_view [label="<__proto__>[ Object ]|<update>update|<close>close"]
ob_model [label="<__proto__>[ Object ]|<value>value=5|<save>save"];
ob_on [label="<__proto__>[ Object ]|<up>up|<down>down|<save>save"];

/* Function Objects */
node [shape = Mrecord, fillcolor=orange, style=filled];
fn_update [label="<__proto__>[update(value)]|\{ console.log(…) \}|<__scope__>[scope]"]
fn_close [label="<__proto__>[close()]|\{ console.log(…) \}|<__scope__>[scope]"]
fn_save [label="<__proto__>[save()]|\{ console.log(…) \}"];
fn_Controller [label="<__proto__>[Controller(model, view)]|\{ view.update(model.value) … \}"]
fn_onUp [label="<__proto__>[onUp()]|\{ model.value++ … \}|<__scope__>[scope]"];
fn_onDown [label="<__proto__>[onDown()]|\{ model.value-- … \}|<__scope__>[scope]"];
fn_onSave [label="<__proto__>[onSave()]|\{ model.save() … \}|<__scope__>[scope]"];

/* References */
ob_view:update -> fn_update:__proto__;
ob_view:close -> fn_close:__proto__;
ob_model:save -> fn_save:__proto__;
top:Controller -> fn_Controller:__proto__;
top:on -> ob_on;
ob_on:up -> fn_onUp:__proto__;
ob_on:down -> fn_onDown:__proto__;
ob_on:save -> fn_onSave:__proto__;
fn_onUp:__scope__ -> closure;
fn_onDown:__scope__ -> closure;
fn_onSave:__scope__ -> closure;
closure:view -> ob_view:__proto__;
closure:model -> ob_model:__proto__;

/* Inheritance Chains */
edge [style=dashed]
closure -> top:__proto__;
}
40 changes: 40 additions & 0 deletions articles/object-graphs-2/factory.js
@@ -0,0 +1,40 @@
//controller
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();
}
};
}
//usage
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);
43 changes: 43 additions & 0 deletions articles/object-graphs-2/prototypal.dot
@@ -0,0 +1,43 @@
digraph finite_state_machine {
size="8.3,5"
rankdir = "LR"


/* Execution Contexts */
node [shape=Mrecord, fillcolor=beige, style=filled];
top [label="<__proto__>[ Scope ]|<Rectangle>Rectangle|<rect>rect|<Square>Square|<sqr>sqr"];

/* Normal Objects */
node [shape = Mrecord, fillcolor=lightskyblue, style=filled];
ob_Rectangle [label = "<__proto__>[ Object ]|<name>name=\"Rectangle\"|<getArea>getArea|<getPerimeter>getPerimeter|<toString>toString"];
ob_Square [label = "<__proto__>[ Object ]|<name>name=\"Square\"|<getArea>getArea|<getPerimeter>getPerimeter"];
ob_rect [label = "<__proto__>[ Object ]|<width>width=6|<height>height=4"];
ob_sqr [label = "<__proto__>[ Object ]|<width>width=5"];

/* Function Objects */
node [shape = Mrecord, fillcolor=orange, style=filled];
fn_getArea [label="<__proto__>[getArea()]|\{return this.width * this.height\}"];
fn_getPerimeter [label="<__proto__>[getPerimeter()]|\{return 2 * (this.width + this.height)\}"];
fn_toString [label="<__proto__>[toString()]|\{return this.name + \" a=\" + …\}"];
fn_getArea2 [label="<__proto__>[getArea()]|\{return this.width * this.width\}"];
fn_getPerimeter2 [label="<__proto__>[getPerimeter()]|\{return 4 * this.width\}"];

/* References */
top:Rectangle -> ob_Rectangle:__proto__;
top:Square -> ob_Square:__proto__;
top:rect -> ob_rect:__proto__;
top:sqr -> ob_sqr:__proto__;
ob_Rectangle:getArea -> fn_getArea:__proto__;
ob_Rectangle:getPerimeter -> fn_getPerimeter:__proto__;
ob_Rectangle:toString -> fn_toString:__proto__;
ob_Square:getArea -> fn_getArea2:__proto__;
ob_Square:getPerimeter -> fn_getPerimeter2:__proto__;

/* Inheritance Chains */
edge [style=dashed]
ob_rect:__proto__ -> ob_Rectangle:__proto__;
ob_sqr:__proto__ -> ob_Square:__proto__;
ob_Square:__proto__ -> ob_Rectangle:__proto__;


}
32 changes: 32 additions & 0 deletions articles/object-graphs-2/prototypal.js
@@ -0,0 +1,32 @@
//rectangle
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();
}
};
//square
var Square = {
name: "Square",
getArea: function getArea() {
return this.width * this.width;
},
getPerimeter: function getPerimeter() {
return this.width * 4;
},
};
Square.__proto__ = Rectangle;
//test
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());

0 comments on commit bf25e9d

Please sign in to comment.