Skip to content

Instantly share code, notes, and snippets.

@juliosantos
Last active August 29, 2015 14:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save juliosantos/dcb163c93a1adfe08d50 to your computer and use it in GitHub Desktop.
Save juliosantos/dcb163c93a1adfe08d50 to your computer and use it in GitHub Desktop.
Versatile prototypical inheritance in JavaScript (+ underscore.js for convenience)
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