Javascript 如何扩展一个类而不必在 ES6 中使用 super?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/31067368/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 06:10:12  来源:igfitidea点击:

How to extend a class without having to use super in ES6?

javascriptclassinheritanceecmascript-6

提问by xhallix

Is it possible to extend a class in ES6 without calling the supermethod to invoke the parent class?

是否可以在 ES6 中扩展类而不调用super调用父类的方法?

EDIT: The question might be misleading. Is it the standard that we have to call super()or am I missing something?

编辑:这个问题可能会产生误导。这是我们必须调用的标准super()还是我遗漏了什么?

For example:

例如:

class Character {
   constructor(){
      console.log('invoke character');
   }
}

class Hero extends Character{
  constructor(){
      super(); // exception thrown here when not called
      console.log('invoke hero');
  }
}

var hero = new Hero();

When I'm not calling super()on the derived class I'm getting a scope problem -> this is not defined

当我不调用super()派生类时,我遇到了范围问题 ->this is not defined

I'm running this with iojs --harmony in v2.3.0

我在 v2.3.0 中使用 iojs --harmony 运行它

回答by loganfsmyth

The rules for ES2015 (ES6) classes basically come down to:

ES2015 (ES6) 类的规则基本上归结为:

  1. In a child class constructor, thiscannot be used until superis called.
  2. ES6 class constructors MUST call superif they are subclasses, or they must explicitly return some object to take the place of the one that was not initialized.
  1. 在子类构造函数中,在调用this之前不能使用super
  2. super如果ES6 类构造函数是子类,则必须调用它们,否则它们必须显式返回某个对象来代替未初始化的对象。

This comes down to two important sections of the ES2015 spec.

这归结为 ES2015 规范的两个重要部分。

Section 8.1.1.3.4defines the logic to decide what thisis in the function. The important part for classes is that it is possible for thisbe in an "uninitialized"state, and when in this state, attempting to use thiswill throw an exception.

8.1.1.3.4节定义了决定this函数中内容的逻辑。类的重要部分是它可能this处于某种"uninitialized"状态,当处于这种状态时,尝试使用this将抛出异常。

Section 9.2.2, [[Construct]], which defines the behavior of functions called via newor super. When calling a base class constructor, thisis initialized at step #8 of [[Construct]], but for all other cases, thisis uninitialized. At the end of construction, GetThisBindingis called, so if superhas not been called yet (thus initializing this), or an explicit replacement object was not returned, the final line of the constructor call will throw an exception.

9.2.2节,[[Construct]]定义了通过new或调用函数的行为super。调用基类构造函数时,this在第 8 步初始化[[Construct]],但对于所有其他情况,this未初始化。在构造结束时,GetThisBinding被调用,因此如果super尚未调用(因此初始化this),或者未返回显式替换对象,则构造函数调用的最后一行将抛出异常。

回答by Amit

There have been multiple answers and comments stating that superMUSTbe the first line inside constructor. That is simply wrong. @loganfsmyth answer has the required references of the requirements, but it boil down to:

有多个答案和评论指出super必须是里面的第一行constructor。那简直是错误的。@loganfsmyth 回答有要求的必要参考,但归结为:

Inheriting (extends) constructor mustcall superbefore using thisand before returning even if thisisn't used

继承 ( extends) 构造函数必须super在使用this之前和返回之前调用,即使this没有使用

See fragment below (works in Chrome...) to see why it might make sense to have statements (without using this) before calling super.

请参阅下面的片段(适用于 Chrome...)以了解为什么this在调用super.

'use strict';
var id = 1;
function idgen() {
  return 'ID:' + id++;
}

class Base {
  constructor(id) {
    this.id = id;
  }

  toString() { return JSON.stringify(this); }
}

class Derived1 extends Base {
  constructor() {
    var anID = idgen() + ':Derived1';
    super(anID);
    this.derivedProp = this.baseProp * 2;
  }
}

alert(new Derived1());

回答by marcel

The new es6 class syntax is only an other notation for "old" es5 "classes" with prototypes. Therefore you cannot instantiate a specific class without setting its prototype (the base class).

新的 es6 类语法只是带有原型的“旧”es5“类”的另一种表示法。因此,您不能在不设置其原型(基类)的情况下实例化特定类。

Thats like putting cheese on your sandwich without making it. Also you cannot put cheese beforemaking the sandwich, so...

那就像把奶酪放在三明治上而不做。做三明治之前你也不能放奶酪,所以......

...using thiskeyword before calling the super class with super()is not allowed, too.

...this在调用超类之前使用关键字super()也是不允许的。

// valid: Add cheese after making the sandwich
class CheeseSandwich extend Sandwich {
    constructor() {
        super();
        this.supplement = "Cheese";
    }
}

// invalid: Add cheese before making sandwich
class CheeseSandwich extend Sandwich {
    constructor() {
        this.supplement = "Cheese";
        super();
    }
}

// invalid: Add cheese without making sandwich
class CheeseSandwich extend Sandwich {
    constructor() {
        this.supplement = "Cheese";
    }
}

If you don't specify a constructor for a base class, the following definition is used:

如果没有为基类指定构造函数,则使用以下定义:

constructor() {}

For derived classes, the following default constructor is used:

对于派生类,使用以下默认构造函数:

constructor(...args) {
    super(...args);
}

EDIT: Found this on developer.mozilla.org:

编辑:发现这个developer.mozilla.org

When used in a constructor, the super keyword appears alone and must be used before the this keyword can be used.

Source

来源

回答by justyourimage

Just registered to post this solution since the answers here don't satisfy me the least since there is actually a simple way around this. Adjust your class-creation pattern to overwrite your logic in a sub-method while using only the super constructorand forward the constructors arguments to it.

刚刚注册发布此解决方案,因为这里的答案至少不能让我满意,因为实际上有一个简单的方法可以解决这个问题。调整您的类创建模式以覆盖子方法中的逻辑,同时仅使用超级构造函数并将构造函数参数转发给它。

As in you do not create an constructor in your subclasses per se but only reference to an method that is overridden in the respective subclass.

就像您在子类中本身不创建构造函数一样,而只是引用在相应子类中被覆盖的方法。

That means you set yourself free from the constructor functionality enforced upon you and refrain to a regular method- that can be overridden and doesn't enforce super() upon you letting yourself the choice if, where and how you want to call super (fully optional) e.g.:

这意味着您将自己从强加给您的构造函数功能中解放出来,并避免使用常规方法- 可以覆盖并且不会在您让自己选择是否、在哪里以及如何调用 super(完全可选)例如:

super.ObjectConstructor(...)

class Observable {
  constructor() {
    return this.ObjectConstructor(arguments);
  }

  ObjectConstructor(defaultValue, options) {
    this.obj = { type: "Observable" };
    console.log("Observable ObjectConstructor called with arguments: ", arguments);
    console.log("obj is:", this.obj);
    return this.obj;
  }
}

class ArrayObservable extends Observable {
  ObjectConstructor(defaultValue, options, someMoreOptions) {
    this.obj = { type: "ArrayObservable" };
    console.log("ArrayObservable ObjectConstructor called with arguments: ", arguments);
    console.log("obj is:", this.obj);
    return this.obj;
  }
}

class DomainObservable extends ArrayObservable {
  ObjectConstructor(defaultValue, domainName, options, dependent1, dependent2) {
    this.obj = super.ObjectConstructor(defaultValue, options);
    console.log("DomainObservable ObjectConstructor called with arguments: ", arguments);
    console.log("obj is:", this.obj);
    return this.obj;
  }
}

var myBasicObservable = new Observable("Basic Value", "Basic Options");
var myArrayObservable = new ArrayObservable("Array Value", "Array Options", "Some More Array Options");
var myDomainObservable = new DomainObservable("Domain Value", "Domain Name", "Domain Options", "Dependency A", "Depenency B");

cheers!

干杯!

回答by Chong Lip Phang

You can omit super() in your subclass, if you omit the constructor altogether in your subclass. A 'hidden' default constructor will be included automatically in your subclass. However, if you do include the constructor in your subclass, super() must be called in that constructor.

如果在子类中完全省略构造函数,则可以在子类中省略 super()。“隐藏”的默认构造函数将自动包含在您的子类中。但是,如果您确实在子类中包含构造函数,则必须在该构造函数中调用 super()。

class A{
   constructor(){
      this.name = 'hello';   
   }
}
class B extends A{
   constructor(){
      // console.log(this.name); // ReferenceError
      super();
      console.log(this.name);
   }
}
class C extends B{}  // see? no super(). no constructor()

var x = new B; // hello
var y = new C; // hello

Read thisfor more information.

阅读本文了解更多信息。

回答by Michael Lewis

The answer by justyourimage is the easiest way, but his example is a little bloated. Here's the generic version:

justyourimage 的回答是最简单的方法,但是他的例子有点臃肿。这是通用版本:

class Base {
    constructor(){
        return this._constructor(...arguments);
    }

    _constructor(){
        // just use this as the constructor, no super() restrictions
    }
}

class Ext extends Base {
    _constructor(){ // _constructor is automatically called, like the real constructor
        this.is = "easy"; // no need to call super();
    }
}

Don't extend the real constructor(), just use the fake _constructor()for the instantiation logic.

不要扩展 real constructor(),只需将 fake_constructor()用于实例化逻辑。

Note, this solution makes debugging annoying because you have to step into an extra method for every instantiation.

请注意,此解决方案使调试变得烦人,因为您必须为每个实例化步入一个额外的方法。

回答by OBDM

I would recommend to use OODK-JSif you intend to develop following OOP concepts.

如果您打算开发以下 OOP 概念,我会建议使用OODK-JS

OODK(function($, _){

var Character  = $.class(function ($, μ, _){

   $.public(function __initialize(){
      $.log('invoke character');
   });
});

var Hero = $.extends(Character).class(function ($, μ, _){

  $.public(function __initialize(){
      $.super.__initialize();
      $.log('invoke hero');
  });
});

var hero = $.new(Hero);
});

回答by Jonathan de M.

Try:

尝试:

class Character {
   constructor(){
     if(Object.getPrototypeOf(this) === Character.prototype){
       console.log('invoke character');
     }
   }
}


class Hero extends Character{
  constructor(){
      super(); // throws exception when not called
      console.log('invoke hero');
  }
}
var hero = new Hero();

console.log('now let\'s invoke Character');
var char = new Character();

Demo

Demo

回答by MyUserInStackOverflow

Simple solution: I think its clear no need for explanation.

简单的解决方案:我认为很明显不需要解释。

class ParentClass() {
    constructor(skipConstructor = false) { // default value is false
        if(skipConstructor) return;
        // code here only gets executed when 'super()' is called with false
    }
}
class SubClass extends ParentClass {
    constructor() {
        super(true) // true for skipping ParentClass's constructor.
        // code
    }
}

回答by tonix

@Bergi mentioned new.target.prototype, but I was looking for a concrete example proving that you can access this(or better, the reference to the object the client code is creating with new, see below) without having to call super()at all.

@Bergi 提到了new.target.prototype,但我正在寻找一个具体的例子来证明您可以访问this(或者更好的是,对客户端代码正在创建的对象的引用new,见下文)而无需调用super()

Talk is cheap, show me the code... So here is an example:

说话很便宜,给我看代码......所以这是一个例子:

class A { // Parent
    constructor() {
        this.a = 123;
    }

    parentMethod() {
        console.log("parentMethod()");
    }
}

class B extends A { // Child
    constructor() {
        var obj = Object.create(new.target.prototype)
        // You can interact with obj, which is effectively your `this` here, before returning
        // it to the caller.
        return obj;
    }

    childMethod(obj) {
        console.log('childMethod()');
        console.log('this === obj ?', this === obj)
        console.log('obj instanceof A ?', obj instanceof A);
        console.log('obj instanceof B ?',  obj instanceof B);
    }
}

b = new B()
b.parentMethod()
b.childMethod(b)

Which will output:

这将输出:

parentMethod()
childMethod()
this === obj ? true
obj instanceof A ? true
obj instanceof B ? true

So you can see that we are effectively creating an object of type B(the child class) which is also an object of type A(its parent class) and within the childMethod()of child Bwe have thispointing to the object objwhich we created in B's constructorwith Object.create(new.target.prototype).

所以,你可以看到,我们正在有效地创建类型的对象B(子类),它也是类型的对象A(它的父类)和内childMethod()子的B,我们已经this指向的对象obj,我们在B的创建constructorObject.create(new.target.prototype)

And all this without caring about superat all.

而这一切都毫不在意super

This leverages the fact that in JS a constructorcan return a completely different object when the client code constructs a new instance with new.

这利用了这样一个事实,即在 JS 中,constructor当客户端代码使用new.

Hope this helps someone.

希望这可以帮助某人。