Nano Hash - криптовалюты, майнинг, программирование

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

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

EDIT: вопрос может ввести в заблуждение. Это стандарт, по которому мы должны называть 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 в версии 2.3.0.


  • Что вы имеете в виду под проблемой масштаба? Вы получаете исключение (и где)? 26.06.2015
  • Я получаю ожидание в своем производном классе при вызове его без вызова super(). Я отредактировал свой вопрос, чтобы сделать его более понятным :) 26.06.2015
  • В какой среде вы это запускаете? 26.06.2015
  • У вас нет выбора, если вы расширяете другой класс, конструктор должен сначала вызвать super(). 26.06.2015
  • @ДжонатандеМ. спасибо, значит, так и должно быть в будущем? 26.06.2015
  • По крайней мере, так это реализовано в Babel. 26.06.2015
  • Это признак того, что ваши объекты не должны принадлежать к одной и той же иерархии наследования: для правильной инициализации состояния объекта должен быть вызван конструктор. Если вам наплевать на состояние - вы просто не должны его продлевать. 26.06.2015
  • @ДжонатандеМ. - Ты не прав. См. это. Вам нужно только иногда звонить super(), не обязательно сначала 26.06.2015
  • @Амит, приятно знать, спасибо! 26.06.2015
  • @Amit, хотя в этом случае это правда, вы обычно не спорите с реализацией, которая не на 100% соответствует спецификации. 26.06.2015
  • @zerkms - 1-й, Джонатан специально указал реализацию Babel. Во-вторых, где в спецификации написано иначе? Для этого есть законные варианты использования 26.06.2015
  • @Amit Моя точка зрения заключалась в следующем: когда обсуждается стандарт, имеет смысл ссылаться на спецификацию, а не на одну из ее неполных реализаций. 26.06.2015
  • @zerkms, но где стандарт указывает, что это незаконно? 26.06.2015
  • @ Амит это не так. Опять же, ваше доказательство было основано на реализации, а не на стандарте. Итак, я упомянул, что если вы хотите заявить что-то в ветке, обсуждающей стандарт, вам следует ссылаться на стандарт, а не на неполную реализацию. 26.06.2015
  • @zerkms - я думаю, что это обсуждение относится к переполнению мета-стека, но в любом случае, как бы вы аргументировали это по-другому? В стандарте нет ссылки на это (очевидно, потому что это не ограничение), поэтому использование образца, чтобы показать, что это работает, является лучшим доказательством, которое я могу придумать, чтобы показать, что это не ограничение. 26.06.2015

Ответы:


1

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

  1. В конструкторе дочернего класса this нельзя использовать до тех пор, пока не будет вызван super.
  2. Конструкторы классов 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) или не был возвращен явный объект замены, последняя строка вызова конструктора вызовет исключение.

26.06.2015
  • Не могли бы вы предложить способ наследования от класса без вызова super()? 28.06.2015
  • @Bergi Хотите уточнить? 28.06.2015
  • Как вы думаете, возможно ли в ES6 наследовать от класса без вызова super() в конструкторе? 28.06.2015
  • Спасибо за редактирование - теперь оно здесь; вы можете сделать return Object.create(new.target.prototype, …), чтобы избежать вызова суперконструктора. 29.03.2016
  • @Bergi, не могли бы вы пояснить, что такое new.target? Я никогда не видел, чтобы такая конструкция использовалась раньше 30.03.2017
  • @Maximus См. Что такое «new.target»? 30.03.2017
  • Вы должны добавить, что произойдет, если конструктор опущен: конструктор по умолчанию будет использоваться в соответствии с MDN. 18.11.2018
  • @Bergi Thanks for the edit - it's right there now; you can do return Object.create(new.target.prototype, …) to avoid calling the super constructor. Не могли бы вы привести пример, пожалуйста? 01.05.2020

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

    26.06.2015
  • "See fragment below (works in Chrome...)" откройте консоль разработчика Chrome и нажмите «Выполнить фрагмент кода»: Uncaught ReferenceError: this is not defined. Конечно, вы можете использовать методы в конструкторе до super(), но вы не можете использовать методы из класса раньше! 26.06.2015
  • то, что вы не можете использовать this перед super() (ваш код доказывает это), не имеет прямого отношения к спецификации, а к реализации javascript. Итак, вы должны вызвать "super" перед "this". 26.06.2015
  • @marcel - я думаю, у нас было много путаницы. Я только заявлял (все время), что использование STATEMENTS перед использованием super является законным, а вы заявляли, что использование this перед вызовом super является незаконным. Мы оба правы, просто не поняли друг друга :-) (И это исключение было преднамеренным, чтобы показать, что это незаконно - я даже назвал свойство «WillFail») 27.06.2015
  • Я бы сказал, что пример быстрый и грязный. Извините за резкость. Это показывает, что super не обязательно должен быть первым оператором. Но зачем мне это? Сгенерировать идентификатор перед вызовом базового конструктора? Почему бы не использовать его для всех классов? derivedProp это null. По крайней мере, вызывает много вопросов. А вы так говорите, будто у вас есть пример практического применения. Но пример пока теоретический. 18.12.2020
  • @x-yuri это ответ пятилетней давности :-) Однако я все еще думаю, что его ценность заключается в объяснении того, что является допустимым или недействительным кодом, а не в том, какой вариант использования существует для такого кода. Больше знать — это всегда хорошо, даже если вы не видите варианта использования в любой момент времени. 21.12.2020

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

    РЕДАКТИРОВАТЬ: Нашел это на developer.mozilla.org:

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

    Источник

    26.06.2015
  • поэтому, если я правильно вас понял, я не смогу использовать какой-либо метод из класса Character в моем классе Hero, если не вызывать super()? Но это кажется не совсем правильным, потому что я могу вызывать методы из базового класса. так что я думаю, мне просто нужно супер при вызове конструктора 26.06.2015
  • 1. В ОП this вообще не используется. 2. JS — это не бутерброд, и в ES5 вы всегда можете использовать this, даже перед вызовом любой другой функции, которая вам нравится (которая может определять или не определять свойство дополнения). 26.06.2015
  • @amit 1. И теперь я тоже не могу использовать this?! 2. Мой класс JS представляет собой бутерброд, а в ES6 нельзя всегда использовать this. Я просто пытаюсь объяснить классы es6 (с помощью метафоры), и никому не нужны такие деструктивные/ненужные комментарии. 26.06.2015
  • @marcel Мои извинения за цинизм, но: 1. речь шла о появлении новой проблемы, которой не было в ОП. 2 - обратить ваше внимание на то, что ваше утверждение неверно (что все еще имеет место) 26.06.2015
  • @marcel - смотри мой ответ 26.06.2015
  • Также нельзя класть сыр перед приготовлением бутерброда, так что... Тем не менее, как ни странно, мы можем положить сыр в бутерброд, даже не имея сыра. class Base {structor () { this.cheese() } } class Derived extends Base { cheese () { console.log('swiss') } } new Derived фактически печатает швейцарский язык. 10.09.2016
  • Мне нравится ваш пример, который имеет смысл для новичков :) 04.10.2019
  • Я думаю, что когда мы расширяем родительский класс, конструктор родительского класса автоматически вызывает их дочерний класс. 10.01.2020

  • 4

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

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

    26.07.2017

    5

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

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

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

    ваше здоровье!

    21.09.2016
  • мне нужно объяснение, как будто мне пять на этот вопрос .. я чувствую, что это очень глубокий ответ, но сложный и поэтому игнорируется 20.08.2017
  • @swyx: магия находится внутри конструктора, где «это» относится к другому типу объекта в зависимости от того, какой тип объекта вы создаете. Например. если вы создаете новый DomainObservable, this.ObjectConstructor ссылается на другой метод, т. е. DomainObserveable.ObjectConstructor ; в то время как если вы создаете новый ArrayObservable, this.ObjectConstructor ссылается на ArrayObservable.ObjectConstructor . 24.08.2017
  • Смотрите мой ответ, я разместил гораздо более простой пример 24.01.2019
  • Я полностью согласен @swyx; этот ответ делает слишком много ... я только просмотрел его, и я уже устал. Я чувствую, что хочу объяснить, как будто мне пять лет И мне действительно нужно пописать ... 12.08.2019

  • 6

    Ответ от 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() для логики инстанцирования.

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

    24.01.2019
  • Да, это, безусловно, самый простой метод и самый ясный ответ - вопрос, который я задаю, это... Почему, Боже, ПОЧЕМУ!?... Есть очень веская причина, почему super() не работает автоматически... вы можете захотеть передать определенные параметры в свой базовый класс, чтобы вы могли выполнить некоторую обработку/логику/мышление перед созданием экземпляра базового класса. 08.03.2019

  • 7

    Пытаться:

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

    Демо

    26.06.2015
  • в этом примере вы также используете super(), и если вы его оставите, у вас возникнет исключение. Поэтому я думаю, что в этом случае невозможно пропустить этот вызов super(). 26.06.2015
  • Я думал, что ваша цель не в том, чтобы выполнить родительский конструктор, это то, что делает этот код. Вы не можете избавиться от super при расширении. 26.06.2015
  • извините за то, что ввел в заблуждение :) Нет, я просто хотел знать, действительно ли необходимо использовать super(), так как я задаюсь вопросом о синтаксисе, потому что в других языках нам не нужно вызывать метод super при вызове конструктора для производного класса 26.06.2015
  • Я думал, что ваша цель не в том, чтобы выполнить родительский конструктор, это то, что делает этот код. --- это не. Он по-прежнему выполняет родительский конструктор. 26.06.2015
  • Согласно developer.mozilla.org/en-US/docs/ Web/JavaScript/Reference/, A constructor *can* use the super keyword to call the constructor of a parent class., так что я бы сказал, дождитесь выпуска ES6 26.06.2015
  • @zerkms Я имел в виду выполнить некоторый код в конструкторе. 26.06.2015
  • поэтому я бы сказал, подождите выпуска ES6 --- он уже был выпущен, ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf 26.06.2015

  • 8

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

    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's constructor.
            // code
        }
    }
    
    06.03.2017
  • Я не уверен, зачем весь приведенный выше шаблонный код, и я не совсем уверен, есть ли у этого подхода побочные эффекты. У меня работает без проблем. 06.03.2017
  • Одна проблема: если вы хотите снова расширить свой подкласс, вам придется встроить функцию skipConstructor в конструктор каждого подкласса. 24.01.2019

  • 10

    @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 (его родительский класс), а внутри childMethod() дочернего B у нас есть this, указывающий на объект obj, который мы создали. в B's constructor с Object.create(new.target.prototype).

    И все это совершенно не заботясь о super.

    Это использует тот факт, что в JS constructor может возвращать совершенно другой объект, когда клиентский код создает новый экземпляр с new.

    Надеюсь, это поможет кому-то.

    01.05.2020
    Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

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

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

    Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
    В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..