Last active
February 29, 2016 17:37
-
-
Save ironboy/fce39acd9266f08dd049 to your computer and use it in GitHub Desktop.
ES5 inheritance using Object.create and similar patterns
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
// 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