Skip to content

Instantly share code, notes, and snippets.

@idiotWu
Last active February 8, 2017 08:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save idiotWu/8f4a378f7e08af8dc002131253f6eaec to your computer and use it in GitHub Desktop.
Save idiotWu/8f4a378f7e08af8dc002131253f6eaec to your computer and use it in GitHub Desktop.
Make built-in classes extendable
// copied from <https://github.com/service-mocker/service-mocker>
function extensify(Native) {
class Extendable {
constructor(...args) {
this._native = initNative(...args);
checkLack(this._native);
}
}
/* istanbul ignore next: safari specfic cases */
function initNative(...args) {
if (typeof Native === 'function') {
return new Native(...args);
}
// avoid `XMLHttpRequest.bind` is undefined in safari 9-
return new Native();
}
let checked = false;
/* istanbul ignore next */
function checkLack(instance) {
if (checked) {
return;
}
// safari 9- only have methods on `XMLHttpRequest.prototype`
// so we need copy properties from an instance
for (let prop in instance) {
if (!Extendable.prototype.hasOwnProperty(prop)) {
Object.defineProperty(Extendable.prototype, prop, {
get() {
return this._native[prop];
},
set(value) {
this._native[prop] = value;
return value;
},
enumerable: true,
configurable: true,
});
}
}
}
// copy all static properties
// safari 9- will include a "prototype" property on XMLHttpRequest
try {
Object.keys(Native).forEach(prop => {
Object.defineProperty(
Extendable, prop,
Object.getOwnPropertyDescriptor(Native, prop)
);
});
} catch (e) {}
// delegate all unset properties to `_native`
(function mapPrototypeMethods(
source = Native.prototype,
target = Extendable.prototype
) {
if (source.constructor === Object) {
// exit recursion
return;
}
Object.keys(source).forEach(prop => {
/* istanbul ignore if */
if (target.hasOwnProperty(prop)) {
return;
}
const descriptor = Object.getOwnPropertyDescriptor(source, prop);
if (descriptor.get || descriptor.set) {
descriptor.get = function getNative() {
return this._native[prop];
};
descriptor.set = function setNative(value) {
this._native[prop] = value;
return value;
};
} else if (typeof descriptor.value === 'function') {
// method
const nativeFn = descriptor.value;
descriptor.value = function wrapped(...args) {
return nativeFn.apply(this._native, args);
};
}
Object.defineProperty(target, prop, descriptor);
});
// recursively look-up
mapPrototypeMethods(Object.getPrototypeOf(source), target);
})();
return Extendable;
}
// copied from <https://github.com/loganfsmyth/babel-plugin-transform-builtin-extend>
function extensify(cls){
function ExtendableBuiltin(){
// Not passing "newTarget" because core-js would fall back to non-exotic
// object creation.
var instance = Reflect.construct(cls, Array.from(arguments));
Object.setPrototypeOf(instance, Object.getPrototypeOf(this));
return instance;
}
ExtendableBuiltin.prototype = Object.create(cls.prototype, {
constructor: {
value: cls,
enumerable: false,
writable: true,
configurable: true,
},
});
if (Object.setPrototypeOf){
Object.setPrototypeOf(ExtendableBuiltin, cls);
} else {
ExtendableBuiltin.__proto__ = cls;
}
return ExtendableBuiltin;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment