Skip to content

Instantly share code, notes, and snippets.

@jmakeig
Last active July 9, 2018 04:13
Show Gist options
  • Save jmakeig/45cf2fa19103f8a870f11895f5350624 to your computer and use it in GitHub Desktop.
Save jmakeig/45cf2fa19103f8a870f11895f5350624 to your computer and use it in GitHub Desktop.
JavaScript inheritance with overrides and extensions
'use strict';
/**
* Default implementation. Specialized classes should
* extend and override/extend on their own prototypes.
*/
function Wrapper(wrapped) {
// Factory, not direct constructor
if(!this) { return new Wrapper(wrapped); }
// Internal (not private) property to track the wrapped object
Object.defineProperty(this, '_wrapped', {
enumerable: false,
configurable: false,
writable: true,
value: wrapped,
});
return this;
}
Object.assign(Wrapper.prototype, {
doIt() { return 'FooWrapper.prototype.doIt'; },
someOther() { return 'FooWrapper.prototype.someOther'; },
});
// Wrapped class
function Foo() {};
Object.assign(
Foo.prototype, {
// Override method from inherited Wrapper.prototype
foo() { return 'Foo.prototype.foo'; },
});
// “Extends” the Wrapper class. (Yes, I should use ES6 classes)
function FooWrapper(wrapped) {
if(!(wrapped instanceof Foo)) { throw new TypeError('Can only wrap a Foo'); }
if(!this) { return new FooWrapper(wrapped); }
// Call the super class factory
return Wrapper.call(this, wrapped);
}
FooWrapper[Symbol.species] = () => Wrapper;
// Inherit the super class’s prototype
FooWrapper.prototype = Object.assign(
Object.create(Wrapper.prototype), {
// Override method from inherited Wrapper.prototype
doIt() { return 'FooWrapper.prototype.doIt' + ' >> ' + this._wrapped.foo(); },
// Extend inherited Wrapper.prototype with new methods
myOwn() { return 'FooWrapper.prototype.myOwn' + ' >> ' + this._wrapped.foo(); }
});
FooWrapper(new Foo()).doIt(); // FooWrapper.prototype.doIt
FooWrapper(new Foo()).someOther(); // Wrapper.prototype.someOther
FooWrapper(new Foo()).myOwn(); // FooWrapper.prototype.myOwn
//Wrapper(new Foo()).myOwn(); // Error!
/*
_The_ definitive way to set up prototypical inheritance in JavaScript (ES2015) without classes.
- Sets constructor property so type reporting is correct, such as in stack traces.
This has no bearing on `instanceof` or `Object.isPrototypeOf()`.
- Compact way to add getters and setters to an extended prototype
*/
function Super() {}
Super.prototype.$1 = function() { /* … */ }
function Custom() {}
Custom.prototype = Object.create(Super.prototype, {
// Cleanest way to set property aspects, like get/set, enumerable, configurable
$2: {
get() { /* … */ }
}
});
Object.assign(Custom.prototype, {
/* ALWAYS set the constructor function */
constructor: Custom,
$3() {}
});
@jmakeig
Copy link
Author

jmakeig commented Jul 9, 2018

Argh. This really should be, simply:

function Super() { /*…*/ }
Super.prototype.duper = function() { /*…*/ }

function Custom() {
  Super.call(this, /*…*/);
}
Custom.prototype = Object.create(Super.prototype);
Custom.prototype.constructor = Custom;
Custom.prototype.doIt = function()  { /*…*/ }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment