Czy można wywołać metodę podstawową z metody prototypowej w JavaScript, jeśli została nadpisana?
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
}
};
javascript
prototype
overriding
Mark Clancy
źródło
źródło
Odpowiedzi:
Nie rozumiem, co dokładnie próbujesz zrobić, ale zwykle implementowanie zachowania specyficznego dla obiektu odbywa się w następujący sposób:
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...*/); }
źródło
Cóż, jednym ze sposobów byłoby zapisanie metody podstawowej, a następnie wywołanie jej z metody nadpisanej
MyClass.prototype._do_base = MyClass.prototype.do; MyClass.prototype.do = function(){ if (this.name === 'something'){ //do something new }else{ return this._do_base(); } };
źródło
_do_base
przechowuje odniesienie do dowolnej funkcji zdefiniowanej wcześniej. Gdyby go nie było, to byłobyundefined
. JavaScript nie jest -make
wyrażenia nigdy nie są oceniane leniwie, tak jak sugerujesz. Teraz, jeśli dziedziczony prototyp również używał _do_base do przechowywania tego, co uważa za implementację swojego typu podstawowegodo
, to masz problem. Więc tak, zamknięcie byłoby lepszym, bezpiecznym dla dziedziczenia sposobem przechowywania odniesienia do oryginalnej implementacji funkcji, jeśli używa się tej metody - chociaż wymagałoby to użyciaFunction.prototype.call(this)
.Obawiam się, że twój przykład nie działa tak, jak myślisz. Ta część:
this.do = function(){ /*do something*/ };
zastępuje definicję
MyClass.prototype.do = function(){ /*do something else*/ };
Ponieważ nowo utworzony obiekt ma już właściwość „do”, nie sprawdza łańcucha prototypów.
Klasyczna forma dziedziczenia w Javascript jest niezręczna i trudna do zrozumienia. Sugerowałbym zamiast tego użycie prostego wzoru dziedziczenia Douglasa Crockforda. Lubię to:
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
Moim zdaniem dużo jaśniejszy sposób obsługi obiektów, konstruktorów i dziedziczenia w javascript. Możesz przeczytać więcej w Crockfords Javascript: The good parts .
źródło
base_do
funkcji, ponieważ w ten sposób tracisz jakiekolwiekthis
powiązanie w oryginalnejdo
metodzie. Tak więc konfiguracja metody podstawowej jest nieco bardziej złożona, zwłaszcza jeśli chcesz ją wywołać za pomocą obiektu podstawowegothis
zamiast obiektu podrzędnego. Sugerowałbym coś podobnegobase_do.apply(me, arguments)
.var
forbase_do
? Czy jest to celowe, a jeśli tak, to dlaczego? I, jak powiedział @GiulioPiancastelli, czy to zrywathis
wiązanie wewnątrz,base_do()
czy to wywołanie odziedziczy pothis
dzwoniącym?var
Powinno tam być. Użyciecall
(lubapply
) umożliwia namthis
prawidłowe wiązanie .Wiem, że ten post jest sprzed 4 lat, ale ze względu na moje tło w języku C # szukałem sposobu na wywołanie klasy bazowej bez konieczności określania nazwy klasy, a raczej uzyskanie jej przez właściwość w podklasie. Więc jedyną moją zmianą w odpowiedzi Christopha byłaby
Od tego:
MyClass.prototype.doStuff.call(this /*, args...*/);
Do tego:
this.constructor.prototype.doStuff.call(this /*, args...*/);
źródło
this.constructor
może nie zawsze wskazywaćMyClass
.jeśli zdefiniujesz taką funkcję (używając OOP)
function Person(){}; Person.prototype.say = function(message){ console.log(message); }
istnieją dwa sposoby wywołania funkcji prototypowej: 1) utwórz instancję i wywołaj funkcję obiektu:
var person = new Person(); person.say('hello!');
a drugi sposób to ... 2) wywołuje funkcję bezpośrednio z prototypu:
Person.prototype.say('hello there!');
źródło
To rozwiązanie wykorzystuje
Object.getPrototypeOf
TestA
jest super, że magetName
TestB
jest dzieckiem, które zastępuje,getName
ale również ma,getBothNames
które wywołuje zarównosuper
wersję,getName
jak ichild
wersję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());
źródło
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(); }; }
źródło
Alternatywa :
// 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); }
źródło
__proto__
których należy unikać używania (które zmienia [[Prototype]] `obiektu).Object.create(...)
Zamiast tego skorzystaj z trasy.Jeśli dobrze rozumiem, zależy Ci na tym, aby funkcjonalność Base była zawsze wykonywana, a część pozostawić wdrożeniom.
Możesz uzyskać pomoc dzięki wzorcowi projektowemu „ metoda szablonu ”.
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 */ }
źródło
Jeśli znasz swoją superklasę z nazwy, możesz zrobić coś takiego:
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();
To się wydrukuje
called foo in Base called foo in Sub called foo in Base
zgodnie z oczekiwaniami.
źródło
Innym sposobem z ES5 jest jawne przejście przez łańcuch prototypów przy użyciu
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()
źródło
Nie, musiałbyś nadać funkcji do w konstruktorze i funkcji do w prototypie różnych nazw.
źródło
Ponadto, jeśli chcesz zastąpić wszystkie wystąpienia, a nie tylko jedno specjalne wystąpienie, ten może pomóc.
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();
wynik:
źródło
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();
źródło