Last active
August 29, 2015 13:56
-
-
Save dalgard/9169923 to your computer and use it in GitHub Desktop.
Extensible constructor pattern (ES5 version) inspired by Backbone.js – NOTE: Using `this.super` multiple times in the inheritance chain creates a problem since it results in a loop. There hasn't yet been an elegant solution to this, but a proper `super` keyword will be supported in ES6. In the meantime, coupling with the super's name can be avoi…
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
function Class(properties) { | |
// This base constructor can be left empty, but a nice boilerplate might look like this | |
if (properties) { | |
// Add properties to the instance | |
Object.getOwnPropertyNames(properties).forEach(function (property_name) { | |
var descriptor = Object.getOwnPropertyDescriptor(properties, property_name); | |
Object.defineProperty(this, property_name, descriptor); | |
}, this); | |
} | |
} | |
// Extensible constructors pattern inspired by Backbone.js | |
Class.extend = function (prototype) { | |
var constructor; | |
// Use constructor function from passed in prototype or from superclass | |
if (prototype && prototype.hasOwnProperty("constructor") && typeof prototype.constructor === "function") { | |
constructor = prototype.constructor; | |
delete prototype.constructor; // This property is re-attached later | |
} | |
else { | |
var self = this; | |
constructor = function Subclass() { return self.apply(this, arguments); }; | |
} | |
// Set up the prototype chain | |
constructor.prototype = Object.create(this.prototype); | |
// Locked down references | |
Object.defineProperties(constructor.prototype, { | |
"constructor": { value: constructor }, | |
"super": { value: this.prototype } | |
}); | |
if (prototype) { | |
// Add extended prototype properties to the subclass | |
Object.getOwnPropertyNames(prototype).forEach(function (property_name) { | |
var descriptor = Object.getOwnPropertyDescriptor(prototype, property_name); | |
Object.defineProperty(constructor.prototype, property_name, descriptor); | |
}); | |
} | |
// Add extend capability and reference to super constructor | |
constructor.extend = this.extend; | |
constructor.super = this; | |
return constructor; | |
}; |
/* Example */
var Animal = Class.extend({
constructor: function Animal(name) { // Named function for better debugging
if (typeof name === "string") console.log("I'm " + name + "!");
else console.log("I am an animal");
},
eats: true
});
var Cat = Animal.extend({
furry: true
});
var miffy = new Cat("Miffy"); // "I'm Miffy!"
// Beware that the values of these properties are shared by instances
miffy.eats; // true
miffy.furry; // true
miffy instanceof Cat; // true
miffy instanceof Animal; // true
/* ... continued from above */
var Mammal = Animal.extend({
constructor: function Mammal() {
// Instance field
this.endothermic_amniote = true;
console.log("Mammal, Ma'am");
// Also call super's constructor function in this context
this.super.constructor.apply(this, arguments);
}
});
var my_cow = new Mammal("mootable"); // "Mammal, Ma'am" "I'm mootable!"
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Attempt at making a class pattern that meets the following demands:
instanceof
)