Last active
August 29, 2015 14:16
-
-
Save juliosantos/dcb163c93a1adfe08d50 to your computer and use it in GitHub Desktop.
Versatile prototypical inheritance in JavaScript (+ underscore.js for convenience)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var App = { | |
Models : {} | |
} | |
// App.Models.Model | |
// Contains methods to be inherited by all other models | |
App.Models.Model = Object.create( {}, { | |
// no model ids shall be reassignable | |
new : {value : function (options, properties) { | |
var id = options.id; // required for get/set to work | |
// extending with options allows for custom attributes on .new | |
return _.extend( Object.create( this, _.extend( properties, { | |
id : { | |
get : function () { return id; }, | |
set : function (value) { | |
if (value && !id) { id = value; } | |
} | |
}, | |
})), options ); | |
}}, | |
edit : {value : function (options) { | |
_.each( options, function (value, key) { | |
if (this.hasOwnProperty( key )) { | |
this[key] = value; | |
} | |
}.bind( this )); | |
return this; | |
}} | |
}); | |
App.Models.Animal = Object.create( App.Models.Model, { | |
// override new to set "class" properties | |
new : {value : function (options) { | |
options = options || {}; | |
options.name = options.name || "Unnamed Animal"; // set default | |
var alive = options.alive || true; // required for get/set to work | |
// super | |
return App.Models.Model.new.call( this, options, { | |
// methods which use the options object must be defined within this | |
// closure so they have access to it | |
originalName : { | |
value : options.name, | |
writable : false // false is default but this makes that explicit | |
}, | |
// this alive method has to be defined within this closure | |
// because of the need for a alive variable | |
alive : { | |
get : function () { return alive; }, | |
set : function (value) { | |
// once the animal is dead, we don't want to allow its revival | |
if (alive == true) { alive = value; } | |
} | |
} | |
}); | |
}}, | |
eat : {value : function () { | |
return this.name + " ate."; | |
}} | |
}); | |
App.Models.Dog = Object.create( App.Models.Animal, { | |
// override new to set "subclass" properties | |
new : {value : function (options) { | |
options = options || {}; | |
options.name = options.name || "Spot"; // set default | |
// super | |
return App.Models.Animal.new.call( this, options ); | |
}}, | |
}); | |
App.Models.Cat = Object.create( App.Models.Animal, { | |
// override methods | |
eat : {value : function () { | |
return this.name + " is on a diet."; | |
}} | |
}); | |
var dog = App.Models.Dog.new(); | |
console.log( dog.name ); // Spot | |
console.log( dog.eat() ); // Spot ate. | |
console.log( dog.originalName ); // Spot | |
dog.originalName = "derp"; | |
console.log( dog.originalName ); // Spot | |
dog.name = "Spot 2.0" | |
console.log( dog.name ); // Spot 2.0 | |
console.log( dog.originalName ); // Spot | |
var will = App.Models.Dog.new( {name : "Will"} ); | |
console.log( will.name ); // Will | |
console.log( will.eat() ); // Will ate. | |
console.log( will.alive ); // true | |
will.alive = false; | |
console.log( will.alive ); // false | |
will.alive = true; | |
console.log( will.alive ); // false | |
var cat = App.Models.Cat.new(); | |
console.log( cat.eat() ); // Unnamed Animal is on a diet. | |
var napoleon = App.Models.Cat.new( {name : "Napoleon"} ); | |
console.log( napoleon.id ); // undefined | |
napoleon.id = 1; | |
console.log( napoleon.id ); // 1 | |
napoleon.id = 2; | |
console.log( napoleon.id ); // 1 | |
var fuzzy = App.Models.Cat.new( {herp: "derp"} ); | |
console.log( fuzzy.herp ); // derp |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment