Как расширить класс, не используя супер в 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
}
}
Ответ 10
Не волнуйтесь, последняя версия браузера Chrome сняла это ограничение. Пожалуйста, проверьте это.