Как расширить класс, не используя супер в ES6?

Можно ли расширить класс в ES6 без вызова метода super для вызова родительского класса?

РЕДАКТИРОВАТЬ: Вопрос может ввести в заблуждение. Это стандарт, который мы должны называть 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

Ответ 1

Правила для классов ES2015 (ES6) в основном сводятся к:

  • В конструкторе дочернего класса this не может использоваться до тех пор, пока не будет вызываться super.
  • Конструкторы классов ES6 ДОЛЖНЫ вызывать super, если они являются подклассами, или они должны явно возвращать некоторый объект, чтобы заменить место, которое не было инициализировано.

Это сводится к двум важным разделам спецификации ES2015.

Раздел 8.1.1.3.4 определяет логику, чтобы решить, что this находится в функции. Важной частью для классов является то, что this возможно в состоянии "uninitialized", и когда в этом состоянии попытка использовать this генерирует исключение.

Раздел 9.2.2, [[Construct]], который определяет поведение функций, вызываемых через new или super. При вызове конструктора базового класса this инициализируется на шаге 8 из [[Construct]], но для всех остальных случаев this не инициализируется. В конце построения вызывается GetThisBinding, поэтому, если super еще не вызывается (таким образом, инициализируя this), или явный объект замены не был возвращен, окончательная строка вызова конструктора вызовет исключение.

Ответ 2

Было несколько ответов и комментариев, в которых указано, что 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());

Ответ 3

Новый синтаксис класса 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);
}

EDIT: найдено на developer.mozilla.org:

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

Источник

Ответ 4

Просто зарегистрирован, чтобы опубликовать это решение, так как ответы здесь не удовлетворяют меня по крайней мере, так как на самом деле это простой способ. Откорректируйте свой шаблон создания класса, чтобы перезаписать свою логику в под-методе, используя только суперструктор и пересылайте ему аргументы конструктора.

Как и вы, вы не создаете конструктор в своих подклассах как таковой, а только ссылку на метод, который переопределяется в соответствующем подклассе.

Это означает, что вы освобождаетесь от функциональности конструктора, навязанной вам и воздерживаетесь от обычного метода, который может быть переопределен и не предусматривает 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");

Ответ 5

Вы можете опустить 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

Прочтите этот для получения дополнительной информации.

Ответ 6

Я бы рекомендовал использовать 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);
});

Ответ 7

Ответ от 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();
    }
}

Не _constructor() реальный constructor(), просто используйте поддельный _constructor() для логики создания экземпляров.

Обратите внимание, что это решение делает отладку раздражающей, потому что вы должны использовать дополнительный метод для каждого экземпляра.

Ответ 8

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\ invoke Character');
var char = new Character();

Демо

Ответ 9

Простое решение: я думаю, что он не нуждается в объяснении.

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 constructor.
        // code
    }
}