An extensible ES6 function with idiomatic inheritance and various other benifits:
npm install --save extensible-function
- When extending
ExtensibleFunction
, the code is idiomatic of extending any ES6 class (no, mucking about with pretend constructors or proxies). - The prototype chain is retained through all subclasses, and
instanceof
/.constructor
return the expected values. .bind()
.apply()
and.call()
all function as expected. This is done by overriding these methods to alter the context of the "inner" function as opposed to theExtensibleFunction
(or it's subclass') instance..bind()
returns a new instance of the functions constructor (be itExtensibleFunction
or a subclass). It usesObject.assign()
to ensure the properties stored on the bound function are consistent with those of the originating function.- Closures are honored, and arrow functions continue to maintain the proper context.
- The "inner" function is stored via a
Symbol
, which is obfuscated by the module.
const ExtensibleFunction = require('extensible-function');
// OR //
import ExtensibleFunction from 'extensible-function';
// Lets extend our `ExtensibleFunction` into an `ExtendedFunction`
class ExtendedFunction extends ExtensibleFunction {
constructor (fn, ...args) {
// Just use `super()` like any other class
// You don't need to pass ...args here, but if you used them
// in the super class, you might want to.
super(fn, ...args);
// Just use `this` like any other class. No more messing with fake return values!
let [constructedPropertyValue, ...rest] = args;
this.constructedProperty = constructedPropertyValue;
}
}
let fn = new ExtendedFunction(function (x) {
// Add `this.y` to `x`
// If either value isn't a number, coax it to one, else it's `0`
return (this.y>>0) + (x>>0)
}, "I'm a constructed property value");
fn.additionalProperty = "I'm an additional property value";
fn instanceof Function; // true
fn instanceof ExtensibleFunction; //true
fn instanceof ExtendedFunction; //true
fn.bind() instanceof Function; //true
fn.bind() instanceof ExtensibleFunction; //true
fn.bind() instanceof ExtendedFunction; //true
fn.constructor == ExtendedFunction; //true
fn.constructedProperty == "I'm a constructed property value"; //true
fn.additionalProperty == "I'm an additional property value"; //true
fn.constructedProperty == fn.bind().constructedProperty; //true
fn.additionalProperty == fn.bind().additionalProperty; //true
fn() == 0; //true
fn(10) == 10; //true
fn.apply({y:10}, [10]) == 20; //true
fn.call({y:10}, 20) == 30; //true
fn.bind({y:30})(10) == 40; //true
The module also provides a convenient constructor which binds a function to itself. Yeah, it sounds weird but the benefit is that calls to these functions have their this
set to themselves.
class BoundExtendedFunction extends ExtensibleFunction.Bound {
constructor (fn, x) {
super(fn);
this.x = x;
}
}
let bfn = new BoundExtendedFunction(function(y){
return this.x + y;
}, 100);
bfn(42); // 142
bfn.x = 0;
bfn(42); // 42