Here's a way using Function::bind
:
const requireNew = (() => {
const kFake = {};
const CtorMap = new WeakMap();
const FuncToString = function toString () {
const info = CtorMap.get(this);
return Function.prototype.toString.apply(
info ? info.ctor : this, arguments);
};
const GetProto = function prototype() {
const info = CtorMap.get(this);
return info ? info.ctor.prototype : undefined;
};
const SetProto = function prototype(prototype) {
const info = CtorMap.get(this);
return !info ? prototype
: info.wrapper.prototype = info.ctor.prototype = prototype;
}
return (Ctor) => {
const wrapper = function () {
if (this === kFake) {
throw new TypeError("Please use 'new' to call this");
}
return Ctor.apply(this, arguments);
}
wrapper.prototype = Ctor.prototype;
const bound = wrapper.bind(kFake);
CtorMap.set(bound, { ctor: Ctor, wrapper });
Object.defineProperties(bound, {
prototype: { get: GetProto, set: SetProto,
enumerable: false, configurable: true },
name: { value: Ctor.name, writable: false,
enumerable: false, configurable: true },
length: { value: Ctor.length, writable: false,
enumerable: false, configurable: true },
toString: { value: FuncToString, writable: true,
enumerable: false, configurable: true }
});
return bound;
}
})();
And here's a simple demo:
function Abc (a) {
this.a = a;
console.log("this =", this, "; .a =", this.a, "; new.target:", new.target);
}
const A = requireNew(Abc);
const a = new A(1);