ES6'da super kullanmak zorunda kalmadan sınıf nasıl uzatılır?


104

ES6'daki bir sınıfı super, ana sınıfı çağırmak için yöntemi çağırmadan genişletmek mümkün müdür ?

DÜZENLEME: Soru yanıltıcı olabilir. Aramamız gereken standart mı super()yoksa bir şeyi mi kaçırıyorum?

Örneğin:

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()Türetilmiş sınıfı çağırmadığımda bir kapsam sorunu alıyorum ->this is not defined

Bunu iojs ile çalıştırıyorum --harmony v2.3.0'da


Dürbün sorunu ne demek? Bir istisna mı yapıyorsunuz (ve nerede)?
Amit

Super () 'i çağırmadan onu çağırırken türetilmiş sınıfımda beklentiyi alıyorum. Daha net hale getirmek için sorumu düzenledim :)
xhallix

Bunu hangi ortamda çalıştırıyorsun?
Amit

6
Başka bir sınıfı genişletirseniz, kurucunun önce super () 'i çağırması gerekir.
Jonathan de M.

@Kafadergisi teşekkür ederim, öyleyse gelecekte olması gerektiği gibi sanırım?
xhallix

Yanıtlar:


153

ES2015 (ES6) sınıflarının kuralları temel olarak şu şekildedir:

  1. Bir alt sınıf yapıcısında, çağrılana thiskadar kullanılamaz super.
  2. ES6 sınıfı oluşturucularının superalt sınıflarsa çağırmaları ZORUNLUdur veya başlatılmamış olanın yerini alması için açıkça bazı nesneler döndürmeleri gerekir.

Bu, ES2015 spesifikasyonunun iki önemli bölümüne gelir.

Bölüm 8.1.1.3.4 , işlevde ne olduğuna karar verme mantığını tanımlar this. Sınıflar için önemli olan kısım this, bir "uninitialized"durumda olmanın mümkün olmasıdır ve bu durumda kullanmaya teşebbüs etmek thisbir istisna atacaktır.

Bölüm 9.2.2 , [[Construct]]aracılığıyla adı işlevleri davranışını tanımlayan newveya super. Bir temel sınıf kurucusunu çağırırken this, adım # 8'de başlatılır [[Construct]], ancak diğer tüm durumlar thisiçin başlatılmamıştır. Yapımın sonunda GetThisBindingçağrılır, bu nedenle superhenüz çağrılmadıysa (böylece başlatılırsa this) veya açık bir değiştirme nesnesi döndürülmediyse, yapıcı çağrısının son satırı bir istisna atar.


1
Aramadan bir sınıftan miras almanın bir yolunu önerebilir misiniz super()?
Bergi

4
ES6'da yapıcıyı çağırmadan bir sınıftan miras almanın mümkün olduğunu düşünüyor musunuz super()?
Bergi

9
Düzenleme için teşekkürler - şu anda orada; return Object.create(new.target.prototype, …)süper kurucuyu aramaktan kaçınmak için yapabilirsiniz .
Bergi


1
Yapıcı atlanırsa ne olacağını eklemelisiniz: MDN'ye göre varsayılan bir kurucu kullanılacaktır .
kleinfreund

12

İçerideki ilk satırın super ZORUNLU olduğunu belirten birden fazla cevap ve yorum var constructor. Bu tamamen yanlış. @loganfsmyth yanıt, gereksinimlerin gerekli referanslarına sahiptir, ancak şu şekilde özetlenebilir:

Devralma ( extends) yapıcısı olmalıdır diyoruz superkullanmadan önce thisve dönmeden önce bile thiskullanılmaz

Aramadan thisönce ifadelerin (kullanmadan ) olmasının neden mantıklı olabileceğini görmek için aşağıdaki parçaya bakın (Chrome'da çalışır ...) 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
"See fragment below (works in Chrome...)"Krom geliştirici konsolunu açın ve "Run kod parçacığı" nı Uncaught ReferenceError: this is not defined. Elbette, yapıcıda yöntemleri daha önce kullanabilirsiniz, super()ancak daha önce sınıftaki yöntemleri kullanamazsınız!
marcel

daha thisönce kullanamayacağınız super()(kodunuz bunu kanıtlıyor) belirtimle hemen hiçbir ilgisi yoktur, ancak javascript uygulamasıyla. Yani, "bundan" önce "süper" demelisiniz.
marcel

@marcel - Çok fazla kafa karışıklığımız olduğunu düşünüyorum. Sadece (başından beri) kullanmadan önce STATEMENTS'e sahip olmanın yasal olduğunu superve thisaramadan önce kullanımın yasa dışı olduğunu söylüyordunuz super. İkimiz de haklıyız, birbirimizi anlamadık :-) (Ve bu istisna kasıtlıydı, neyin yasal olmadığını göstermek için - hatta mülke 'WillFail' adını verdim)
Amit

9

Yeni es6 sınıfı sözdizimi, prototiplerle "eski" es5 "sınıfları için yalnızca başka bir gösterimdir. Bu nedenle, prototipini (temel sınıf) ayarlamadan belirli bir sınıfı başlatamazsınız.

Bu, sandviçinizi yapmadan peynir koymak gibi. Ayrıca peynir koyamazsınız önce , yani sandviç yaparken ...

... thisile süper sınıfı çağırmadan önce anahtar sözcüğün kullanılmasına super()da izin verilmiyor.

// 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";
    }
}

Bir temel sınıf için bir yapıcı belirtmezseniz, aşağıdaki tanım kullanılır:

constructor() {}

Türetilmiş sınıflar için aşağıdaki varsayılan yapıcı kullanılır:

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

DÜZENLEME: Bunu şurada buldum developer.mozilla.org:

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

Kaynak


bu yüzden sizi doğru anlarsam, super () 'i çağırmadığım sürece, Hero sınıfımdaki Character sınıfından herhangi bir yöntemi kullanamayacağım? Ancak bu tam olarak doğru görünmüyor, çünkü yöntemleri temel sınıftan çağırabilirim. bu yüzden
kurucuyu

1. OP'de thishiç kullanılmaz. 2. JS bir sandviç değildir ve ES5'te istediğiniz thisherhangi bir işlevi çağırmadan önce bile kullanabilirsiniz (bu ek özelliği tanımlayabilir veya tanımlamayabilir)
Amit

@amit 1. Ve şimdi de kullanamıyorum this?! 2. JS sınıfım bir sandviçi temsil ediyor ve ES6'da her zaman kullanamazsınız this. Sadece es6 sınıflarını (bir metaforla) açıklamaya çalışıyorum ve kimsenin bu kadar yıkıcı / gereksiz yorumlara ihtiyacı yok.
marcel

@marcel Kinizm için özür dilerim, ancak: 1. OP'de olmayan yeni bir sorunu ortaya koymakla ilgiliydi. 2, iddianızın yanlış olduğuna dikkatinizi çekmek içindir (ki durum hala geçerli)
Amit

@marcel - Cevabımı gör
Amit

4

Bu çözümü göndermek için yeni kayıt oldum, çünkü buradaki cevaplar beni hiç tatmin etmiyor çünkü aslında bunun etrafında basit bir yol var. Yalnızca süper yapıcıyı kullanırken bir alt yöntemde mantığınızın üzerine yazmak için sınıf oluşturma modelinizi ayarlayın ve kurucu argümanlarını ona iletin.

Sizin de olduğu gibi, kendi alt sınıflarınızda bir kurucu yaratmayın, ancak yalnızca ilgili alt sınıfta geçersiz kılınan bir yöntemi referans alın.

Bu, kendinize uygulanan yapıcı işlevselliğinden kendinizi özgür kıldığınız ve normal bir yöntemi kullanmaktan kaçındığınız anlamına gelir - bu, geçersiz kılınabilir ve süper ( isteğe bağlı) örneğin:

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

şerefe!


2
bu konuda bir "beşmişim gibi açıkla" ya ihtiyacım var .. Bunun çok derin bir cevap olduğunu ancak karmaşık olduğunu ve bu yüzden görmezden gelinmiş gibi hissediyorum
swyx

@swyx: sihir, oluşturucunun içindedir; burada 'bu', ne tür bir nesne oluşturduğunuza bağlı olarak farklı bir nesne türünü ifade eder. Örneğin, yeni bir DomainObservable oluşturuyorsanız, this.ObjectConstructor farklı bir yöntemi, yani DomainObserveable.ObjectConstructor; yeni bir ArrayObservable oluşturuyorsanız this.ObjectConstructor, ArrayObservable.ObjectConstructor'a başvurur.
cobberboy

Cevabımı görün, çok daha basit bir örnek yayınladım
Michael Lewis

Tamamen katılıyorum @swyx; bu cevap çok şey yapıyor ... sadece gözden geçirdim ve zaten yorgunum. "Beşmişim gibi açıkla VE gerçekten işemem gerekiyor ..." gibi hissediyorum
spb

4

Yapıcıyı alt sınıfınızda tamamen çıkarırsanız, alt sınıfınızda super () atlayabilirsiniz. Alt sınıfınıza otomatik olarak 'gizli' bir varsayılan kurucu dahil edilecektir. Ancak, yapıcıyı alt sınıfınıza dahil ederseniz, o kurucuda super () çağrılmalıdır.

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

Daha fazla bilgi için bunu okuyun .


2

Resminizin cevabı en kolay yoldur, ancak örneği biraz şişirilmiş. İşte genel sürüm:

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

Gerçeği genişletmeyin, constructor()sahte _constructor()mantığı somutlaştırma mantığı için kullanın .

Unutmayın, bu çözüm hata ayıklamayı can sıkıcı hale getirir çünkü her örnekleme için ekstra bir yönteme adım atmanız gerekir.


Evet, bu açık arayla en kolay yöntem ve en net cevap - sorduğum soru ... "Neden, Tanrım, NEDEN?" ... super () 'in otomatik olmaması için çok geçerli bir neden var. .. temel sınıfınıza belirli parametreleri iletmek isteyebilirsiniz, böylece temel sınıfı başlatmadan önce biraz işleme / mantık / düşünme yapabilirsiniz.
LFLFM

1

Deneyin:

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


bu örnekte ayrıca super () kullanıyorsunuz ve onu bırakırsanız, bir istisna atmış olursunuz. Bu nedenle, bu durumda bu süper () aramayı atlamanın mümkün olmadığını düşünüyorum
xhallix

Amacınızın ana kurucuyu yürütmek olmadığını sanıyordum, bu kodun yaptığı bu. Genişletirken süperden kurtulamazsınız.
Jonathan de M.

yanıltıcı olduğu için üzgünüm :) Hayır, sözdizimini merak ettiğim için super () kullanmanın gerçekten gerekli olup olmadığını bilmek istedim çünkü diğer dillerde türetilmiş sınıf için kurucuyu çağırırken süper yöntemi çağırmak zorunda değiliz
xhallix

1
Developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… 'e göre , A constructor *can* use the super keyword to call the constructor of a parent class.ES6 sürümünü bekleyin derim
Jonathan de M.

3
"Ben ES6 serbest bırakılması için beklemek söyleyebilirim bu yüzden" --- zaten, zaten serbest bırakıldı ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf
zerkms

1

Aşağıdaki OOP konseptlerini geliştirmeyi düşünüyorsanız OODK-JS'yi kullanmanızı tavsiye ederim .

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

1

Basit çözüm: Açıkça açıklamaya gerek olmadığını düşünüyorum.

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

Yukarıdaki standart kodun neden olduğundan emin değilim ve bu yaklaşımın yan etkileri olup olmadığından tam olarak emin değilim. Benim için sorunsuz çalışıyor.
MyUserInStackOverflow

1
Bir sorun: SubClass'ınızı tekrar genişletmek isterseniz, skipConstructor özelliğini her SubClass 'kurucusuna eklemeniz gerekir
Michael Lewis

1

@Bergi bahsetti new.target.prototype, ancak aramaya gerek kalmadan erişebileceğinizi this(veya daha iyisi, müşteri kodunun oluşturduğu nesnenin referansına new, aşağıya bakın) kanıtlayan somut bir örnek arıyordum super().

Konuşmak ucuz, bana kodu göster ... İşte bir örnek:

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)

Hangi çıktı:

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

Eğer etkili bir türde bir nesne oluştururken görebilirsiniz Yani Baynı zamanda tip bir amacı (çocuk sınıfı) A(üst sınıfında) ve içindeki childMethod()çocuğun Bbiz sahip thisnesneyi işaret objbiz B'nin oluşturulan constructorile Object.create(new.target.prototype).

Ve bunların hepsini hiç umursamadan super.

Bu, JS'de constructor, istemci kodu ile yeni bir örnek oluşturduğunda tamamen farklı bir nesne döndürebileceği gerçeğinden yararlanır new.

Umarım bu birine yardımcı olur.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.