6

Some ES6 features are really easy to polyfill:

if(!Array.prototype.find){
  Array.prototype.find=...
}

How would you polyfill new.target? It triggers a syntax error when it's used in an unsupported browser. try/catch doesn't work because it's a syntax error. I don't have to use new.target, I'm mostly just curious.

Leo Jiang
  • 24,497
  • 49
  • 154
  • 284

2 Answers2

6

As Jaromanda commented, you cannot polyfill new syntax, but you can easily work around some new.target use cases for now

Taking a look at the new.target docs you'll see some examples that can easily be written with es5

with new.target

function Foo() {
  if (!new.target) throw "Foo() must be called with new";
  console.log("Foo instantiated with new");
}

Foo(); // throws "Foo() must be called with new"
new Foo(); // logs "Foo instantiated with new"

without

function Foo() {
  if (!(this instanceof Foo)) throw "Foo() must be called with new";
  console.log("Foo instantiated with new");
}

Foo(); // throws "Foo() must be called with new"
new Foo(); // logs "Foo instantiated with new"

with new.target

class A {
  constructor() {
    console.log(new.target.name);
  }
}

class B extends A { constructor() { super(); } }

var a = new A(); // logs "A"
var b = new B(); // logs "B"

without

class A {
  constructor() {
    // class forces constructor to be called with `new`, so
    // `this` will always be set
    console.log(this.constructor.name);
  }
}

class B extends A { constructor() { super(); } }

var a = new A(); // logs "A"
var b = new B(); // logs "B"

Hope this helps a little

Mulan
  • 129,518
  • 31
  • 228
  • 259
  • 1
    No, `!(this instanceof Foo)` is not a complete replacement for `new.target`. – Bergi Oct 27 '15 at 09:15
  • @Bergi could you elaborate? I highly respect your input on this site. – Mulan Oct 27 '15 at 15:53
  • I don't mean to say that it's a complete replacement but it covers two really common cases (as described on MDN). – Mulan Oct 27 '15 at 16:00
  • 1
    Yeah, I guess `instanceof` is fine and covers the typical mistake case, but still [`new.target`](http://stackoverflow.com/q/32450516/1048572) behaves different - e.g. on `foo=new Foo; foo.constructor()`. – Bergi Oct 27 '15 at 16:18
  • One case instanceof doesn't cover is abstract class instantiation. that would be `constructor(){ if (new.target === MyAbstractClass){throw TypeError('Abstract Class cannot be instantiated');` – motobói Sep 19 '16 at 18:50
  • 3
    You can rewrite an abstract class constructor like `if (this.constructor.name === MyAbstractClass.name) {throw new Error("Cannot instantiate abstract class.");}` It works great when extending ES6 classes with an abstract class. I had to do this since Babel still doesn't polyfill `new.target` so my Webpack UglifyJS plugin was reading it as a syntax error, breaking my production builds. – Chunky Chunk Apr 18 '17 at 18:59
0

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);
Dahan Gong
  • 182
  • 8