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

有没有可能在不调用 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 运行这个程序—— Harmony 在 v2.3.0中

141963 次浏览

试试:

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

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

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

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

// 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上找到这个:

在构造函数中使用时,super 关键字将单独出现,并且必须在使用 this 关键字之前使用它。

来源

有多个答案和评论说 super 必须constructor内的第一行。这是完全错误的。@ loganfsmyth 回答包含需求的必要参考信息,但归结起来就是:

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

参见下面的片段(可以在 Chrome 中使用...) ,看看为什么在调用 super之前使用语句(不使用 this)是有意义的。

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

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

  1. 在子类构造函数中,在调用 super之前不能使用 this
  2. 如果 ES6类构造函数是子类,那么它们必须调用 super,或者它们必须显式返回一些对象来代替未初始化的对象。

这涉及到 ES2015规范的两个重要部分。

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

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

只是注册发布这个解决方案,因为这里的答案不让我满意,因为实际上有一个简单的方法解决这个问题。并将构造函数参数转发给它。

因为您不会在子类中创建构造函数本身,而只是引用在相应子类中被重写的方法。

这意味着你把自己从强加于你的构造函数功能中解放出来,避免使用常规的方法 ——这个方法可以被覆盖,并且不强制你自己选择是否、在哪里以及如何调用 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");

干杯!

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

简单的解决办法: 我认为这很明显不需要解释。

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

如果您在子类中完全省略了构造函数,那么您可以在子类中省略 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

阅读 这个了解更多信息。

这个问题的答案是最简单的方法,但是他的例子有点臃肿。下面是一个通用的版本:

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()作为实例化逻辑。

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

@ 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(它的父类)的对象,在子 BchildMethod()中,我们有 this指向我们在 B 的 constructor中用 Object.create(new.target.prototype)创建的对象 obj

而这一切根本不关心 super

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

希望这对谁有帮助。