diff --git a/lib/internal/util.js b/lib/internal/util.js index 10844b645455dd..76ef759cefbc83 100644 --- a/lib/internal/util.js +++ b/lib/internal/util.js @@ -186,3 +186,23 @@ exports.toLength = function toLength(argument) { const len = toInteger(argument); return len <= 0 ? 0 : Math.min(len, Number.MAX_SAFE_INTEGER); }; + +// Useful for Wrapping an ES6 Class with a constructor Function that +// does not require the new keyword. For instance: +// class A { constructor(x) {this.x = x;}} +// const B = createClassWrapper(A); +// B() instanceof A // true +// B() instanceof B // true +exports.createClassWrapper = function createClassWrapper(type) { + const fn = function(...args) { + return Reflect.construct(type, args, new.target || type); + }; + // Mask the wrapper function name and length values + Object.defineProperties(fn, { + name: {value: type.name}, + length: {value: type.length} + }); + Object.setPrototypeOf(fn, type); + fn.prototype = type.prototype; + return fn; +}; diff --git a/test/parallel/test-internal-util-classwrapper.js b/test/parallel/test-internal-util-classwrapper.js new file mode 100644 index 00000000000000..52b3c2b0a9b50a --- /dev/null +++ b/test/parallel/test-internal-util-classwrapper.js @@ -0,0 +1,31 @@ +// Flags: --expose-internals +'use strict'; + +require('../common'); +const assert = require('assert'); +const util = require('internal/util'); + +const createClassWrapper = util.createClassWrapper; + +class A { + constructor(a, b, c) { + this.a = a; + this.b = b; + this.c = c; + } +} + +const B = createClassWrapper(A); + +assert.strictEqual(typeof B, 'function'); +assert(B(1, 2, 3) instanceof B); +assert(B(1, 2, 3) instanceof A); +assert(new B(1, 2, 3) instanceof B); +assert(new B(1, 2, 3) instanceof A); +assert.strictEqual(B.name, A.name); +assert.strictEqual(B.length, A.length); + +const b = new B(1, 2, 3); +assert.strictEqual(b.a, 1); +assert.strictEqual(b.b, 2); +assert.strictEqual(b.c, 3);