Skip to content

Instantly share code, notes, and snippets.

@isaacs
Created April 11, 2010 20:04
Show Gist options
  • Save isaacs/363025 to your computer and use it in GitHub Desktop.
Save isaacs/363025 to your computer and use it in GitHub Desktop.
// wrap in a closure to get at the global,
// no matter where we are.
(function () { this.env = this.env || "prod" })()
// in production mode, be fast.
// in development/testing mode, add some error checking.
function Class (ctor, parent, proto) {
if (typeof parent === "object") {
proto = parent
parent = null
}
proto = proto || {}
if (env === "prod") {
// no type checking, no function.apply
return Extend(ctor, parent, proto)
}
// debug mode. Add the "new" check, and being slow is ok.
function c () {
if (!(this instanceof c)) {
throw new Error("Constructors must be called with 'new'")
}
ctor.apply(this, arguments)
}
return Extend(c, parent, proto)
}
function Extend (child, parent, proto) {
var existing = child.prototype
if (parent) {
child.prototype = Object.create(parent.prototype)
child.parent = parent.prototype
parent.prototype.constructor = parent
for (var p in proto) child.prototype[p] = proto[p]
} else child.prototype = proto
for (var i in existing) child.prototype[i] = existing[i]
child.prototype.constructor = child
return child
}
// need this
if (typeof Object.create !== "function") Object.create = function (p) {
function f () {}
f.prototype = p
return new f
}
// birth and death make the best OOP examples.
function Animal (species) {
this.species = species
this.alive = true
}
Class(Animal,
{ reproduce : function () {
var c = new this.constructor
c.species = this.species
return c
}
, kill : function () {
for (var i in this) {
// can't do things when you're dead
if (typeof this[i] === "function") this[i] = null
}
this.alive = false
}
})
function Mammal (species) {
this.parent.constructor.call(this, species)
}
Class(Mammal, Animal,
{ reproduce : function (partner) {
if (!partner) return null
if (partner.species !== this.species) return null
if (
(this.sex === "M" && partner.sex !== "F")
|| (this.sex === "F" && partner.sex !== "M")
|| (this.sex !== "M" && this.sex !== "F")
) {
return null
}
// wrong time of the month?
if (Math.floor(Math.random() * 100) < 40) return null
var c = this.parent.reproduce.call(this)
, sex = Math.floor(Math.random() * 101)
c.sex = (sex > 50) ? "M"
: (sex < 50) ? "F"
: "X"
c.mother = this.sex === "M" ? partner : this
c.father = this.sex === "M" ? this : partner
return c
}
})
// alternative construction pattern,
// put the constructor right in the Class() call
var Human = Class
( function (name, sex, wealth) {
this.parent.constructor.call(this, "homo sapiens")
this.name = name
this.sex = sex ? String(sex).toUpperCase().charAt(0) : "X"
this.wealth = parseFloat(wealth) || 0
this.children = []
}
, Mammal
, { reproduce : function (partner, boyName, girlName) {
var c = this.parent.reproduce.call(this, partner)
c.name = c.sex === "F" ? girlName : boyName
this.adopt(c)
partner.adopt(c)
return c
}
, adopt : function (child) {
this.children.push(child)
// kids are expensive!
this.wealth /= 2
partner.wealth /= 2
}
, kill : function () {
this.parent.kill.call(this)
if (!this.children.length) return
var estate = this.wealth / this.children.length
this.children.forEach(function (child) { child.wealth += estate })
}
, work : function (wage, howLong) {
var self = this
setTimeout(function () { self.wealth += howLong * wage }, howLong)
}
}
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment