Created
August 7, 2013 22:08
-
-
Save bdragon/6179263 to your computer and use it in GitHub Desktop.
Inheritance model with support for inheriting static properties, prototype properties, and ECMAScript 5 getters/setters, as well as for calling "super". See http://www.bryandragon.com/articles/playing-with-javascript-inheritance
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
/** | |
* Object.extend() | |
* @author Bryan Dragon | |
* @website www.bryandragon.com | |
* @license MIT License | |
*/ | |
(function () { | |
'use strict'; | |
var hasOwn = Object.prototype.hasOwnProperty | |
, descriptorProps = | |
'get,set,value,writable,enumerable,configurable'.split(','); | |
// Add the ability to invoke "super" from within the given function | |
function withSuper(fn, _super) { | |
return function () { | |
var callSuper = this.callSuper; | |
this.callSuper = _super; | |
var value = fn.apply(this, arguments); | |
this.callSuper = callSuper; | |
return value; | |
}; | |
} | |
if (typeof Object.extend !== 'function') { | |
Object.extend = function (Parent, protoProps, staticProps) { | |
var prototype, Child; | |
if (typeof Parent !== 'function' && arguments.length < 3) { | |
staticProps = protoProps; | |
protoProps = Parent; | |
Parent = function () {}; | |
} | |
protoProps || (protoProps = {}); | |
staticProps || (staticProps = {}); | |
Child = function () { | |
if (this.initialize) { | |
this.initialize.apply(this, arguments); | |
} | |
}; | |
// Inherit static properties | |
for (var name in Parent) { | |
if (hasOwn.call(Parent, name)) { | |
Child[name] = Parent[name]; | |
} | |
} | |
// Add static properties | |
for (var name in staticProps) { | |
Child[name] = staticProps[name]; | |
} | |
// Build child prototype | |
function ctor() { this.constructor = Child; } | |
ctor.prototype = Parent.prototype; | |
prototype = new ctor; | |
// Add instance properties to prototype | |
for (var name in protoProps) { | |
// Define ECMAScript5 properties for properties prefixed | |
// with `$` | |
if (name.substr(0, 1) === '$' && | |
(typeof protoProps[name] === 'object' || | |
typeof protoProps[name] === 'function')) { | |
var descriptor = protoProps[name]; | |
name = name.substr(1); | |
// Support closures | |
if (typeof descriptor === 'function') { | |
descriptor = descriptor(); | |
} | |
// Allow getter/setter inheritance | |
var parentDescriptor = | |
Object.getOwnPropertyDescriptor(Parent.prototype, name); | |
if (parentDescriptor) { | |
var hasValue = (hasOwn.call(descriptor, 'value') || | |
hasOwn.call(descriptor, 'writable')); | |
var hasAccessor = (hasOwn.call(descriptor, 'get') || | |
hasOwn.call(descriptor, 'set')); | |
descriptorProps.forEach(function (prop) { | |
if (prop === 'get' || prop === 'set') { | |
if (! hasValue) { | |
if (typeof parentDescriptor[prop] === 'function') { | |
if (typeof descriptor[prop] === 'function') { | |
descriptor[prop] = | |
withSuper(descriptor[prop], parentDescriptor[prop]); | |
} | |
else { | |
descriptor[prop] = parentDescriptor[prop]; | |
} | |
} | |
} | |
} | |
else if (prop === 'value' || prop === 'writable') { | |
if (! hasAccessor) { | |
if (! hasOwn.call(descriptor, prop) && | |
hasOwn.call(parentDescriptor, prop)) { | |
descriptor[prop] = parentDescriptor[prop]; | |
} | |
} | |
} | |
else { | |
if (! hasOwn.call(descriptor, prop) && | |
hasOwn.call(parentDescriptor, prop)) { | |
descriptor[prop] = parentDescriptor[prop]; | |
} | |
} | |
}); | |
} | |
Object.defineProperty(prototype, name, descriptor); | |
} | |
// Allow instance methods to call `this.callSuper()` | |
else if (typeof protoProps[name] === 'function' && | |
typeof prototype[name] === 'function') { | |
prototype[name] = | |
withSuper(protoProps[name], Parent.prototype[name]); | |
} | |
// Otherwise, just copy the property | |
else { | |
prototype[name] = protoProps[name]; | |
} | |
} | |
// Apply child prototype | |
Child.prototype = prototype; | |
// Provide reference to parent prototype, | |
// as is convention elsewhere | |
Child.__super__ = Parent.prototype; | |
// Allow inheritance directly from child class | |
Child.extend = function (protoProps, staticProps) { | |
return Object.extend(Child, protoProps, staticProps); | |
} | |
return Child; | |
}; | |
} | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment