Skip to content

Instantly share code, notes, and snippets.

@ironboy
Last active February 29, 2016 17:37
Show Gist options
  • Save ironboy/fce39acd9266f08dd049 to your computer and use it in GitHub Desktop.
Save ironboy/fce39acd9266f08dd049 to your computer and use it in GitHub Desktop.
ES5 inheritance using Object.create and similar patterns
// First things first - polyfills for IE
// Thomas' minimal Object.assign polyfill
Object.assign = Object.assign || function(){
for(var i = 1; i < arguments.length; i++){
for(var j in arguments[i] || {}){
arguments[0][j] = arguments[i][j];
}
}
};
// ...and Douglas Crockford's original Object.create
Object.create = Object.create || function (o) {
function F() {}
F.prototype = o;
return new F();
};
// ..and John Resig's getPrototypeOf polyfill
if ( typeof Object.getPrototypeOf !== "function" ) {
if ( typeof "test".__proto__ === "object" ) {
Object.getPrototypeOf = function(object){
return object.__proto__;
};
} else {
Object.getPrototypeOf = function(object){
// May break if the constructor has been tampered with
return object.constructor.prototype;
};
}
}
/// --------------------------------------------------------------
// Object create is nice but it has a funky second argument
// that requires us to define each property in a cumbersome way:
var base = {name:"John Doe"};
var newObj = Object.create(base, {
greeting: { writable: true, configurable: true, value: "Hello" }
});
// What we would like is just to be able to add object properties
// in an easy way (like Object.assign), so this is one solution
function oCreate(extend,props){
return Object.assign(Object.create(extend),props);
}
// Now we can do this instead - which is less cumbersome:
var newObj2 = oCreate(base,{greeting:"Hello"});
// If we would like to see constructor names in the console
// (nice for debugging) we would need to modify Crockford's code like this
Object.createWithConstrName = function(constrName,obj) {
if(!constrName){
obj = obj || constrName;
function F() {}
}
else {
eval ("var F = function " + constrName + '(){}');
obj.constructor = F;
}
F.prototype = obj;
return new F();
};
/// --------------------------------------------------------------
// And nice way to handle inheritance is by creating a base object
// with an extend method
var Base = {
extend: function(props){
// A new object with this object as its prototype
var obj = Object.createWithConstrName(this.className,this);
// Assign properties to the new object
Object.assign(obj,props);
return obj;
}
};
// If we really need to call super methods we can fix this too
Base.super = function(){
var func = arguments.callee.caller, funcName, p = this;
// find a prototype method that has the same name
// but is not the same function as the caller func
do {
if(!funcName){
for(var i in p){
if(p[i] === func){funcName = i;}
}
}
if(p && funcName && p[funcName] !== func){
return (p[funcName] || function(){}).apply(this,arguments);
}
p = Object.getPrototypeOf(p);
} while (p !== {});
};
// Now we can do things in a clean object oriented way
var Organism = Base.extend({
className: "Organism",
alive: true,
greet: function(){
return "Hi I am " + (this.alive ? "alive" : "dead");
}
});
var Animal = Organism.extend({
className: "Animal",
canMove: true,
greet:function(){
return this.super() + ' and I ' + (this.canMove ? "can move" : "can't move");
}
});
var Dog = Animal.extend({
className: "Dog",
name: "Fido",
barks: true,
greet: function(){
return this.super() + ' and I ' + (this.barks ? "bark" : "don't bark");
}
});
var aDog = Dog.extend({name:"Puppe"});
var aDeadDog = Dog.extend({name:"Muppe",barks:false,canMove:false,alive:false});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment