Extending ES2015 through prototype inheritance [duplicate]

泪湿孤枕 提交于 2020-12-15 05:44:20

问题


I am making a library where I would like to provide a helper to extend the base class so that users can extend it even if they are not using ES2015 themselves. The issue is that if the base class happens to be in ES2015 (because it isn't sent through transpiler), my calling of parent constructor from subclass fails with:

Class constructor Foo cannot be invoked without 'new'

Example of what I am trying to achieve:

class Foo {
  constructor() {
    console.log("In Foo");
  }

  superMethod() {
    return console.log("In super method");
  }

  static classMethod() {
    return console.log("In class method");
  }
}

Foo.extendClass = function (constructor, methods) {
  const currentClass = this;
  if (typeof constructor !== 'function') {
    methods = constructor;
    constructor = function () {
      return Object.getPrototypeOf(constructor).apply(this, arguments);
    };
  }
  constructor.prototype = Object.create(currentClass.prototype, {
    constructor: {
      value: constructor,
      writable: true,
      configurable: true
    }
  });
  Object.setPrototypeOf(constructor, currentClass);
  Object.assign(constructor.prototype, methods);
  return constructor;
};

const Bar = Foo.extendClass({
  subMethod: function() {
    return console.log("In sub method");
  }
});

b = new Bar();

The problem is in this line:

return Object.getPrototypeOf(constructor).apply(this, arguments);

So how can I call into parent's constructor? I thought ES2015 are just a sugar on top of standard prototype inheritance, but it looks like you cannot simulate it? Is there another way to define subclasses at run time?


回答1:


ES6 created a clearer distinction between [[Call]] and [[Construct]] operations. ES6 classes can only be constructed using a [[Construct]] operation, but .call/.apply are [[Call]] operations. To use [[Construct]], you'll need to use Reflect.construct() to create the new instance. e.g.

constructor = function () {
  return Reflect.construct(Object.getPrototypeOf(constructor), arguments);
};

or

constructor = function () {
  return Reflect.construct(currentClass, arguments);
};

I'll also note however that this also ignores another feature new in ES6, which is new.target. You'll also want to preserve that, by doing

constructor = function () {
  return Reflect.construct(currentClass, arguments, new.target);
};

to ensure that the things behave the same way as class syntax would.

Also note that you must not use this inside this function. It is the return value of Reflect.construct that is the new instance, so if you wanted to work with it in your own custom constructor, you'll want to do

constructor = function () {
  const _this = Reflect.construct(currentClass, arguments, new.target);

  _this.foo = "foo";
  return _this;
};

however, if you think about it, this is essentially all the same as doing

constructor = class extends currentClass {}


来源:https://stackoverflow.com/questions/58050619/extending-es2015-through-prototype-inheritance

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!