Javascript alt sınıftan bir üst yöntemi nasıl çağırılır?


156

Son birkaç saatimi sorunuma bir çözüm bulmaya çalışarak geçirdim ama umutsuz görünüyor.

Temelde bir alt sınıftan bir ebeveyn yöntemi çağırmak için bilmek gerekir. Şimdiye kadar denedim tüm şeyler ya çalışma ya da üst yöntemi yazarak sona erer.

Javascript OOP kurmak için aşağıdaki kodu kullanıyorum:

// SET UP OOP
// surrogate constructor (empty function)
function surrogateCtor() {}

function extend(base, sub) {
    // copy the prototype from the base to setup inheritance
    surrogateCtor.prototype = base.prototype;
    sub.prototype = new surrogateCtor();
    sub.prototype.constructor = sub;
}

// parent class
function ParentObject(name) {
    this.name = name;
}
// parent's methods
ParentObject.prototype = {
    myMethod: function(arg) {
        this.name = arg;
    }
}

// child
function ChildObject(name) {
    // call the parent's constructor
    ParentObject.call(this, name);
    this.myMethod = function(arg) {
        // HOW DO I CALL THE PARENT METHOD HERE?
        // do stuff
    }
}

// setup the prototype chain
extend(ParentObject, ChildObject);

Önce ebeveynin yöntemini çağırmak ve daha sonra çocuk sınıfta bazı şeyler eklemek gerekir.

Çoğu OOP dilinde çağırmak kadar basit olurdu parent.myMethod() ama javascript'te nasıl yapıldığını gerçekten kavrayamıyorum.

Herhangi bir yardım çok takdir, teşekkür ederim!

Yanıtlar:


196

İşte nasıl yapılır: ParentClass.prototype.myMethod();

Veya geçerli örnek bağlamında çağırmak isterseniz, şunları yapabilirsiniz: ParentClass.prototype.myMethod.call(this)

Aynı şey, alt sınıftan argümanlarla bir üst yöntemi çağırmak için de geçerlidir: ParentClass.prototype.myMethod.call(this, arg1, arg2, ..)* İpucu: bağımsız değişkenleri dizi olarak geçirmek apply()yerine kullanın call().


7
Geçerli örnek bağlamında çağırmak istiyorsanız, ParentClass.prototype.myMethod.apply() or kurucunuzda yaptığınız gibi ParentClass.prototype.myMethod.call () `yi yapmanız gerekir.
JMM

3
Sadece argümanlarla çağrı yapmak istiyorsanız, uygula veya çağır fonksiyonuna ( ParentClass.prototype.myMethod.call(this, arg1, arg2, arg3...);) girerler
Gershom

Anlamıyorum. ParentClass.prototype.myMethod.call (this) öğesini çağırırsam; ChildObject's myMethod, "Yakalanmayan TypeError: undefined 'call' özelliği okunamıyor" hatası aldım.
zhekaus

@zhekaus, bu myMethodsizin sınıfınızda olmadığı anlamına gelir .
YemSalat

2
Şu anda bir nesne yöntemi bildirmek için this.myFun = function () {} kullanıyorum, bu yüzden ParentClass.prototype.myFun.call (...) çalışmıyor, bu yüzden CurrentClass.prototype.myFun.call ( ...). JS ... bir saçmalık, gerçek OOP kullanmalıyız.
Loenix

156

ES6 stili, superanahtar kelime gibi yeni özellikler kullanmanızı sağlar . superanahtar kelime, ES6 sınıfları sözdizimini kullanırken tamamen ana sınıf bağlamıyla ilgilidir. Çok basit bir örnek olarak, ödeme:

class Foo {
    static classMethod() {
        return 'hello';
    }
}

class Bar extends Foo {
    static classMethod() {
        return super.classMethod() + ', too';
    }
}
Bar.classMethod(); // 'hello, too'

Ayrıca, superüst yapıcıyı aramak için kullanabilirsiniz :

class Foo {}

class Bar extends Foo {
    constructor(num) {
        let tmp = num * 2; // OK
        this.num = num; // ReferenceError
        super();
        this.num = num; // OK
    }
}

Ve elbette üst sınıf özelliklerine erişmek için kullanabilirsiniz super.prop. Bu yüzden ES6'yı kullanın ve mutlu olun.


10
@ fsinisi90 İnanıyorum ki, soru ebeveynin sınıf yöntemleri ile ilgili değil, daha ziyade ES6'dan itibaren süper anahtar kelimeyle çağrılamayan ebeveynin örnek yöntemleri ile ilgili.
mcmlxxxiii

statik olmayan yöntemler için de çalışır (Chrome ile test edilmiştir, transpiliing olmadan, statik anahtar kelimeyi denemez)
Gianluca Casati

Neden superçağrılması gerekiyor ? “Eski” JS'de bir denklik var mı?
1252748

3
super () her şeyden önce alt sınıf kurucuda çağrılmalıdır.
user938363

1
@GianlucaCasati: sadece super()statik yöntemlerle kullanabilirsiniz ; yapıcıda kullandığınız anlaşılıyor.
ZzZombo

5

Bunu yapmak için Class, ES6'nın soyutlanmasıyla sınırlı değilsiniz. Üst yapıcı prototip yöntemlerine erişmek __proto__özelliği ile amortismana tabi tutulur (amortismana tabi olduğunu şikayet etmek için diğer JS kodlayıcıları olacağından eminim) ki bu amortismana tabi tutulur, ancak aynı zamanda bunun aslında alt sınıflandırma ihtiyaçları için gerekli bir araç olduğunu keşfetmiştir ( özellikle Array alt sınıflandırma ihtiyaçları için). Bu yüzden __proto__özellik hala bildiğim tüm büyük JS motorlarında kullanılabilir olsa da, ES6 Object.getPrototypeOf()bunun üstüne işlevselliği tanıttı . super()Aracını Classsoyutlama bu bir sözdizimsel şekerdir.

Dolayısıyla, üst yapıcı adına erişiminiz yoksa ve Classsoyutlamayı kullanmak istemiyorsanız yine de aşağıdaki gibi yapabilirsiniz;

function ChildObject(name) {
    // call the parent's constructor
    ParentObject.call(this, name);
    this.myMethod = function(arg) {
    //this.__proto__.__proto__.myMethod.call(this,arg);
    Object.getPrototypeOf(Object.getPrototypeOf(this)).myMethod.call(this,arg);
    }
}

4

Birden fazla kalıtım düzeyi olması durumunda, bu işlev diğer dillerde bir super () yöntemi olarak kullanılabilir. İşte bir demo keman , bazı testlerle, yöntem kullanımınızda böyle kullanabilirsiniz:call_base(this, 'method_name', arguments);

Oldukça yeni ES işlevlerinden yararlanır, eski tarayıcılarla uyumluluk garanti değildir. IE11, FF29, CH35'te test edilmiştir.

/**
 * Call super method of the given object and method.
 * This function create a temporary variable called "_call_base_reference",
 * to inspect whole inheritance linage. It will be deleted at the end of inspection.
 *
 * Usage : Inside your method use call_base(this, 'method_name', arguments);
 *
 * @param {object} object The owner object of the method and inheritance linage
 * @param {string} method The name of the super method to find.
 * @param {array} args The calls arguments, basically use the "arguments" special variable.
 * @returns {*} The data returned from the super method.
 */
function call_base(object, method, args) {
    // We get base object, first time it will be passed object,
    // but in case of multiple inheritance, it will be instance of parent objects.
    var base = object.hasOwnProperty('_call_base_reference') ? object._call_base_reference : object,
    // We get matching method, from current object,
    // this is a reference to define super method.
            object_current_method = base[method],
    // Temp object wo receive method definition.
            descriptor = null,
    // We define super function after founding current position.
            is_super = false,
    // Contain output data.
            output = null;
    while (base !== undefined) {
        // Get method info
        descriptor = Object.getOwnPropertyDescriptor(base, method);
        if (descriptor !== undefined) {
            // We search for current object method to define inherited part of chain.
            if (descriptor.value === object_current_method) {
                // Further loops will be considered as inherited function.
                is_super = true;
            }
            // We already have found current object method.
            else if (is_super === true) {
                // We need to pass original object to apply() as first argument,
                // this allow to keep original instance definition along all method
                // inheritance. But we also need to save reference to "base" who
                // contain parent class, it will be used into this function startup
                // to begin at the right chain position.
                object._call_base_reference = base;
                // Apply super method.
                output = descriptor.value.apply(object, args);
                // Property have been used into super function if another
                // call_base() is launched. Reference is not useful anymore.
                delete object._call_base_reference;
                // Job is done.
                return output;
            }
        }
        // Iterate to the next parent inherited.
        base = Object.getPrototypeOf(base);
    }
}

2

Douglas Crockford fikrine dayanan bir şeye ne dersiniz:

    function Shape(){}

    Shape.prototype.name = 'Shape';

    Shape.prototype.toString = function(){
        return this.constructor.parent
            ? this.constructor.parent.toString() + ',' + this.name
            : this.name;
    };


    function TwoDShape(){}

    var F = function(){};

    F.prototype = Shape.prototype;

    TwoDShape.prototype = new F();

    TwoDShape.prototype.constructor = TwoDShape;

    TwoDShape.parent = Shape.prototype;

    TwoDShape.prototype.name = '2D Shape';


    var my = new TwoDShape();

    console.log(my.toString()); ===> Shape,2D Shape

2

Alt nesnelerin JavaScript prototip zincirini kullanarak üst özelliklere ve yöntemlere erişmesinin güzel bir yolu ve Internet Explorer ile uyumludur. JavaScript, prototip zincirinde yöntemler arar ve çocuğun prototip zincirinin şöyle görünmesini isteriz:

Alt örnek -> Çocuğun prototipi (Alt yöntemlerle) -> Ebeveynin prototipi (Üst yöntemlerle) -> Nesne prototipi -> null

Alt yöntemler, aşağıdaki üç yıldızda *** gösterildiği gibi gölgeli üst yöntemleri de çağırabilir.

Bunu nasıl yapacağınız aşağıda açıklanmıştır:

//Parent constructor
function ParentConstructor(firstName){
    //add parent properties:
    this.parentProperty = firstName;
}

//add 2 Parent methods:
ParentConstructor.prototype.parentMethod = function(argument){
    console.log(
            "Parent says: argument=" + argument +
            ", parentProperty=" + this.parentProperty +
            ", childProperty=" + this.childProperty
    );
};

ParentConstructor.prototype.commonMethod = function(argument){
    console.log("Hello from Parent! argument=" + argument);
};

//Child constructor    
function ChildConstructor(firstName, lastName){
    //first add parent's properties
    ParentConstructor.call(this, firstName);

    //now add child's properties:
    this.childProperty = lastName;
}

//insert Parent's methods into Child's prototype chain
var rCopyParentProto = Object.create(ParentConstructor.prototype);
rCopyParentProto.constructor = ChildConstructor;
ChildConstructor.prototype = rCopyParentProto;

//add 2 Child methods:
ChildConstructor.prototype.childMethod = function(argument){
    console.log(
            "Child says: argument=" + argument +
            ", parentProperty=" + this.parentProperty +
            ", childProperty=" + this.childProperty
    );
};

ChildConstructor.prototype.commonMethod = function(argument){
    console.log("Hello from Child! argument=" + argument);

    // *** call Parent's version of common method
    ParentConstructor.prototype.commonMethod(argument);
};

//create an instance of Child
var child_1 = new ChildConstructor('Albert', 'Einstein');

//call Child method
child_1.childMethod('do child method');

//call Parent method
child_1.parentMethod('do parent method');

//call common method
child_1.commonMethod('do common method');


1

Çok seviyeli prototip araması için çok daha kolay ve daha kompakt bir çözüm var, ancak Proxydestek gerekiyor. Kullanım: SUPER(<instance>).<method>(<args>)örneğin, iki sınıf varsayarak için Ave B extends Ayöntemi ile m: SUPER(new B).m().

function SUPER(instance) {
    return new Proxy(instance, {
        get(target, prop) {
            return Object.getPrototypeOf(Object.getPrototypeOf(target))[prop].bind(target);
        }
    });
}

0

Eğer ebeveyn prototip ile ana yöntemini çağırabilirsiniz ederken, kullandığınız geçerli çocuk örneğini geçmesi gerekecektir call, applyya bindyöntemini. Bu bindyöntem yeni bir işlev yaratacaktır, bu yüzden performansa önem veriyorsanız, yalnızca bir kez çağrılmasını önermiyorum.

Alternatif olarak, child yöntemini değiştirebilir ve orijinal child yöntemini çağırırken üst yöntemi örneğe koyabilirsiniz.

function proxy(context, parent){
  var proto = parent.prototype;
  var list = Object.getOwnPropertyNames(proto);
  
  var child = {};
  for(var i=0; i<list.length; i++){
    var key = list[i];

    // Create only when child have similar method name
    if(context[key] !== proto[key]){
      child[key] = context[key];
      context[key] = function(){
        context.super = proto[key];
        return child[key].apply(context, arguments);
      }
    }
  }
}

// ========= The usage would be like this ==========

class Parent {
  first = "Home";

  constructor(){
    console.log('Parent created');
  }

  add(arg){
    return this.first + ", Parent "+arg;
  }
}

class Child extends Parent{
  constructor(b){
    super();
    proxy(this, Parent);
    console.log('Child created');
  }

  // Comment this to call method from parent only
  add(arg){
    return this.super(arg) + ", Child "+arg;
  }
}

var family = new Child();
console.log(family.add('B'));

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.