JavaScript prototipini kullanarak yöntem çağırma


129

Geçersiz kılınmışsa JavaScript'teki bir prototip yönteminden temel yöntemi çağırmak mümkün müdür?

MyClass = function(name){
    this.name = name;
    this.do = function() {
        //do somthing 
    }
};

MyClass.prototype.do = function() {  
    if (this.name === 'something') {
        //do something new
    } else {
        //CALL BASE METHOD
    }
};

Temel yöntem hangisidir? Yapıcıda this.do = function () {}?
Ionuț G. Stan

Yanıtlar:


200

Tam olarak ne yapmaya çalıştığınızı anlamadım, ancak normalde nesneye özgü davranışı uygulamak şu satırlar boyunca yapılır:

function MyClass(name) {
    this.name = name;
}

MyClass.prototype.doStuff = function() {
    // generic behaviour
}

var myObj = new MyClass('foo');

var myObjSpecial = new MyClass('bar');
myObjSpecial.doStuff = function() {
    // do specialised stuff
    // how to call the generic implementation:
    MyClass.prototype.doStuff.call(this /*, args...*/);
}

1
Bunu anlamadım, buraya markvpc ile tamamen aynı amaç için geldim. Demek istediğim, prototipteki diğer yöntemlerden kullanılan bir tür "özel" yönteme sahip olmak istiyorum. Cevabınız beni bir fabrika kurmaya zorluyor ve bundan kaçınmanın bir yolu olup olmadığını bilmek istiyorum.
R01010010

10
Karışıklığa neden olduğundan JS'de "Sınıf" kelimelerini kullanmayın. JavaScript'te sınıf yok
Polaris

1
Bu yalnızca somutlaştırılmış nesneler için çalışır. Ya tüm örnekleri geçersiz kılmak istiyorsanız?
supertonsky

Benim için çalışıyor! Tam olarak ihtiyacım olan şey. Bir ebeveynden miras alan 4 kurucum var. Bunlardan ikisinin, prototipleri olan temel oluşturucuda bir yöntemi geçersiz kılmaları ve çağırmaları gerekir. Bu bana tam olarak bunu yapmamı sağladı.
binarygiant

3
Bunun neden bu kadar çok olumlu oy aldığını ve doğru olarak işaretlendiğini anlamıyorum. Bu, alt sınıfın tüm örneklerini değil, yalnızca temel sınıfın bir örneğinin davranışını geçersiz kılar. Dolayısıyla bu soruya cevap vermiyor.
BlueRaja - Dany Pflughoeft

24

Bunu yapmanın bir yolu, temel yöntemi kaydetmek ve ardından onu geçersiz kılma yönteminden çağırmaktır.

MyClass.prototype._do_base = MyClass.prototype.do;
MyClass.prototype.do = function(){  

    if (this.name === 'something'){

        //do something new

    }else{
        return this._do_base();
    }

};

11
Bu sonsuz özyineleme yaratacaktır.
Johan Tidén

3
Orada bulundum: Sonsuz özyineleme.
jperelli

Else cümlesi, prototype.do'ya bir referans olan _do_base'i çağırıyor ve çalıştırıyor. Hiçbir parametre (veya durum) değişmediğinden, kod yeniden diğerine gidecek ve _do_base'i yeniden çağıracaktır.
Johan Tidén

10
@ JohanTidén, _do_basedaha önce tanımlanmış olan fonksiyona bir referans saklar. Hiçbiri olmasaydı, o zaman olurdu undefined. JavaScript değildir — makeifadeler asla sizin önerdiğiniz gibi tembel olarak değerlendirilmez. Şimdi, miras alınan prototip aynı zamanda temel türün gerçeklemesi olduğunu düşündüğü şeyi depolamak için _do_base kullanıyorsa do, o zaman bir sorununuz var demektir. Bu nedenle, evet, bir kapatma, bu yöntemi kullanıyorsanız orijinal işlev uygulamasına referansı depolamanın daha iyi ve kalıtım açısından güvenli bir yolu olacaktır - ancak bu kullanılması gerektirir Function.prototype.call(this).
binki

16

Korkarım örneğiniz düşündüğünüz gibi çalışmıyor. Bu kısım:

this.do = function(){ /*do something*/ };

tanımının üzerine yazar

MyClass.prototype.do = function(){ /*do something else*/ };

Yeni oluşturulan nesne zaten bir "do" özelliğine sahip olduğundan, prototip zincirine bakmaz.

Javascript'teki klasik miras biçimi garip ve anlaşılması zor. Bunun yerine Douglas Crockfords basit kalıtım modelini kullanmanızı öneririm. Bunun gibi:

function my_class(name) {
    return {
        name: name,
        do: function () { /* do something */ }
    };
}

function my_child(name) {
    var me = my_class(name);
    var base_do = me.do;
    me.do = function () {
        if (this.name === 'something'){
            //do something new
        } else {
            base_do.call(me);
        }
    }
    return me;
}

var o = my_child("something");
o.do(); // does something new

var u = my_child("something else");
u.do(); // uses base function

Bence javascript'te nesneleri, yapıcıları ve kalıtımı işlemenin çok daha net bir yolu. Crockfords Javascript: The good parts'da daha fazlasını okuyabilirsiniz .


3
Gerçekten çok güzel, ancak gerçekten base_dobir işlev olarak çağıramazsınız , çünkü thisorijinal doyöntemdeki herhangi bir bağlantıyı bu şekilde kaybedersiniz . Bu nedenle, temel yöntemin kurulumu, özellikle alt nesne thisyerine temel nesneyi kullanarak çağırmak istiyorsanız, biraz daha karmaşıktır . Benzer bir şey öneririm base_do.apply(me, arguments).
Giulio Piancastelli

Sadece merak, neden kullanmayın variçin base_do? Bu bilerek mi ve öyleyse neden? Ve @GiulioPiancastelli'nin dediği gibi, bu çağrı thisiçindeki bağlantıyı kırar base_do()mı yoksa bu çağrı thisarayan kişiden miras alır mı?
binki

@binki Örneği güncelledim. varOlmalıdır. call(Veya apply) kullanmak thisdüzgün bir şekilde bağlanmamızı sağlar .
Magnar

10

Bu gönderinin 4 yıl öncesine ait olduğunu biliyorum, ancak C # arka planım nedeniyle, sınıf adını belirtmek yerine alt sınıfta bir özellik tarafından elde etmek zorunda kalmadan temel sınıfı çağırmanın bir yolunu arıyordum. Bu yüzden Christoph'un cevabındaki tek değişikliğim olurdu

Bundan:

MyClass.prototype.doStuff.call(this /*, args...*/);

Buna:

this.constructor.prototype.doStuff.call(this /*, args...*/);

6
Bunu yapma, this.constructorher zaman işaret etmeyebilir MyClass.
Bergi

Birisi mirası ayarlamakta hata yapmadıkça olmalı. "this" her zaman en üst düzey nesneyi ifade etmeli ve "yapıcı" özelliği her zaman kendi yapıcı işlevini göstermelidir.
Triynko

5

bunun gibi bir işlev tanımlarsanız (OOP kullanarak)

function Person(){};
Person.prototype.say = function(message){
   console.log(message);
}

bir prototip işlevi çağırmanın iki yolu vardır: 1) bir örnek oluşturun ve nesne işlevini çağırın:

var person = new Person();
person.say('hello!');

ve diğer yol ... 2) işlevi doğrudan prototipten çağırmaktır:

Person.prototype.say('hello there!');

5

Bu çözüm kullanır Object.getPrototypeOf

TestA olan süper getName

TestBgeçersiz kılma bir çocuktur getNameama, aynı zamanda sahip getBothNamesolduğu aramaları supersürümü getNameyanı sıra childversiyon

function TestA() {
  this.count = 1;
}
TestA.prototype.constructor = TestA;
TestA.prototype.getName = function ta_gn() {
  this.count = 2;
  return ' TestA.prototype.getName is called  **';
};

function TestB() {
  this.idx = 30;
  this.count = 10;
}
TestB.prototype = new TestA();
TestB.prototype.constructor = TestB;
TestB.prototype.getName = function tb_gn() {
  return ' TestB.prototype.getName is called ** ';
};

TestB.prototype.getBothNames = function tb_gbn() {
  return Object.getPrototypeOf(TestB.prototype).getName.call(this) + this.getName() + ' this object is : ' + JSON.stringify(this);
};

var tb = new TestB();
console.log(tb.getBothNames());


4
function NewClass() {
    var self = this;
    BaseClass.call(self);          // Set base class

    var baseModify = self.modify;  // Get base function
    self.modify = function () {
        // Override code here
        baseModify();
    };
}

4

Bir alternatif :

// shape 
var shape = function(type){
    this.type = type;
}   
shape.prototype.display = function(){
    console.log(this.type);
}
// circle
var circle = new shape('circle');
// override
circle.display = function(a,b){ 
    // call implementation of the super class
    this.__proto__.display.apply(this,arguments);
}

İşe yaramasına rağmen, bu çözümü önermeyeceğim. __proto__(Bir nesnenin [[Prototip]] 'nı değiştiren) kullanımından kaçınılması için birçok neden vardır . Lütfen Object.create(...)bunun yerine rotayı kullanın.
KarelG

2

Doğru anladıysam, Base işlevselliğinin her zaman gerçekleştirilmesini istersiniz, ancak bunun bir parçası uygulamalara bırakılmalıdır.

' Şablon yöntemi ' tasarım modelinden yardım alabilirsiniz .

Base = function() {}
Base.prototype.do = function() { 
    // .. prologue code
    this.impldo(); 
    // epilogue code 
}
// note: no impldo implementation for Base!

derived = new Base();
derived.impldo = function() { /* do derived things here safely */ }

1

Süper sınıfınızı adıyla biliyorsanız, şöyle bir şey yapabilirsiniz:

function Base() {
}

Base.prototype.foo = function() {
  console.log('called foo in Base');
}

function Sub() {
}

Sub.prototype = new Base();

Sub.prototype.foo = function() {
  console.log('called foo in Sub');
  Base.prototype.foo.call(this);
}

var base = new Base();
base.foo();

var sub = new Sub();
sub.foo();

Bu yazdıracak

called foo in Base
called foo in Sub
called foo in Base

beklenildiği gibi.


1

ES5 ile başka bir yol, prototip zincirini açık bir şekilde geçmektir. Object.getPrototypeOf(this)

const speaker = {
  speak: () => console.log('the speaker has spoken')
}

const announcingSpeaker = Object.create(speaker, {
  speak: {
    value: function() {
      console.log('Attention please!')
      Object.getPrototypeOf(this).speak()
    }
  }
})

announcingSpeaker.speak()

0

Hayır, yapıcıdaki do işlevine ve prototipteki do işlevine farklı adlar vermeniz gerekir.


0

Ek olarak, yalnızca bir özel örneği değil, tüm örnekleri geçersiz kılmak istiyorsanız , bu yardımcı olabilir.

function MyClass() {}

MyClass.prototype.myMethod = function() {
  alert( "doing original");
};
MyClass.prototype.myMethod_original = MyClass.prototype.myMethod;
MyClass.prototype.myMethod = function() {
  MyClass.prototype.myMethod_original.call( this );
  alert( "doing override");
};

myObj = new MyClass();
myObj.myMethod();

sonuç:

doing original
doing override

-1
function MyClass() {}

MyClass.prototype.myMethod = function() {
  alert( "doing original");
};
MyClass.prototype.myMethod_original = MyClass.prototype.myMethod;
MyClass.prototype.myMethod = function() {
  MyClass.prototype.myMethod_original.call( this );
  alert( "doing override");
};

myObj = new MyClass();
myObj.myMethod();
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.