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

时间:2015-06-26 07:25:09

标签: javascript class inheritance ecmascript-6

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

编辑:这个问题可能会产生误导。这是我们必须致电super()的标准还是我错过了什么?

例如:

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();

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

我用iojs运行这个 - 在v2.3.0中的和谐

10 个答案:

答案 0 :(得分:130)

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

  1. 在子类构造函数中,this在调用super之前无法使用。
  2. ES6类构造函数必须调用super,如果它们是子类,或者它们必须显式返回一些对象来取代未初始化的对象。
  3. 这归结为ES2015规范的两个重要部分。

    8.1.1.3.4节定义了决定函数中this是什么的逻辑。类的重要部分是this可能处于"uninitialized"状态,而在此状态下,尝试使用this将引发异常。

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

答案 1 :(得分:10)

有多个答案和评论声明super 必须 constructor内的第一行。这完全是错的。 @loganfsmyth答案具有要求的必要参考,但归结为:

继承(extends)构造函数必须在使用super之前调用this并在返回之前即使未使用this

请参阅下面的片段(适用于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());

答案 2 :(得分:6)

新的es6类语法只是带有原型的“旧”es5“类”的另一种表示法。因此,如果不设置其原型(基类),则无法实例化特定类。

就像把奶酪放在你的三明治上而不制作它。你也不能把之前的奶酪放在制作三明治之前,所以......

...在使用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";
    }
}

如果未指定基类的构造函数,则使用以下定义:

constructor() {}

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

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

编辑:在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

答案 3 :(得分:4)

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

在你自己中,不要在子类中创建构造函数,而只引用在相应子类中重写的方法。

这意味着您将自己设置为免于强制执行的构造函数并避免使用常规方法 - 可以覆盖并且在您自行选择时不强制执行super(),如果,您想在何处以及如何调用超级(完全可选),例如:

[true]

super.ObjectConstructor(...)

喝彩!

答案 4 :(得分:4)

如果在子类中完全省略构造函数,则可以在子类中省略super()。 A'隐藏'默认构造函数将自动包含在您的子类中。但是,如果在子类中包含构造函数,则必须在该构造函数中调用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

阅读this了解详情。

答案 5 :(得分:1)

如果您打算开发以下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);
});

答案 6 :(得分:1)

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

对话很便宜,请告诉我代码...这是一个示例:

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)

将输出:

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

因此您可以看到我们正在有效地创建B类型的对象(子类),它也是A类型的对象(其父类)并且在{{1}内子childMethod()的},我们有B指向我们用this在B的obj中创建的对象constructor

这一切根本不关心Object.create(new.target.prototype)

这利用了这样的事实:在JS中,super可以在客户端代码使用constructor构造新实例时返回完全不同的对象。

希望这对某人有帮助。

答案 7 :(得分:0)

尝试:

locations = Location.arel_table
hotels = Hotel.arel_table

countries = Location
                .select(locations[:country].as("keyword"))
                .joins(:hotels)
                .where(hotels[:email].not_eq(nil))
                .where(locations[:country].matches("#{search}%"))

cities = Location
            .select(locations[:city].as("keyword"))
            .joins(:hotels)
            .where(hotels[:email].not_eq(nil))
            .where(locations[:city].matches("#{search}%"))

union = countries.union(cities)

result = Location.from(locations.create_table_alias(union, :locations).to_sql)

Demo

答案 8 :(得分:0)

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

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
    }
}

答案 9 :(得分:0)

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

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();
    }
}

不要扩展真实的constructor(),只需将伪造的_constructor()用于实例化逻辑即可。

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