Metode panggilan menggunakan prototipe JavaScript

129

Apakah mungkin untuk memanggil metode dasar dari metode prototipe dalam JavaScript jika sudah ditimpa?

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
    }
};
markvpc
sumber
Yang merupakan metode dasar? this.do = function () {} di konstruktor?
Ionuț G. Stan

Jawaban:

200

Saya tidak mengerti apa sebenarnya yang Anda coba lakukan, tetapi biasanya menerapkan perilaku objek spesifik dilakukan di sepanjang baris ini:

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...*/);
}
Christoph
sumber
1
Saya tidak mengerti, saya datang ke sini persis untuk tujuan yang sama dengan markvpc. Maksud saya, saya ingin memiliki semacam metode "pribadi" yang digunakan dari metode lain dalam prototipe. Respons Anda memaksa saya untuk membuat pabrik dan saya ingin tahu apakah ada cara untuk menghindarinya.
R01010010
10
Jangan gunakan kata-kata "Kelas" di JS karena itu menciptakan kebingungan. Tidak ada kelas dalam JavaScript
Polaris
1
Ini hanya berfungsi untuk objek yang dipakai. Bagaimana jika Anda ingin mengganti semua instance?
supertonsky
Bekerja untukku! Apa yang saya butuhkan. Saya memiliki 4 konstruktor yang mewarisi dari orangtua. Dua dari mereka perlu mengganti dan memanggil metode pada konstruktor dasar yang merupakan prototipe mereka. Ini memungkinkan saya untuk persis seperti itu.
binarygiant
3
Saya tidak mengerti mengapa ini memiliki begitu banyak upvotes dan ditandai sebagai benar. Ini hanya mengesampingkan perilaku untuk satu instance dari kelas dasar, tidak semua instance dari kelas bawah. Jadi ini tidak menjawab pertanyaan.
BlueRaja - Danny Pflughoeft
24

Nah salah satu cara untuk melakukannya adalah menyimpan metode dasar dan kemudian memanggilnya dari metode utama, seperti itu

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

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

        //do something new

    }else{
        return this._do_base();
    }

};
Ramuns Usovs
sumber
11
Ini akan membuat rekursi tak terbatas.
Johan Tidén
3
Saya pernah ke sana: Rekursi tak terbatas.
jperelli
Klausa lain memanggil dan menjalankan _do_base, yang merupakan referensi ke prototype.do. Karena tidak ada parameter (atau status) yang berubah, kode akan kembali masuk ke yang lain, memanggil _do_base lagi.
Johan Tidén
10
@ JohanTidén _do_basemenyimpan referensi ke fungsi apa pun yang didefinisikan sebelumnya. Jika tidak ada, maka itu akan terjadi undefined. JavaScript bukan — makeekspresi tidak pernah dievaluasi dengan malas seperti yang Anda sarankan. Sekarang, jika prototipe yang diwarisi dari juga menggunakan _do_base untuk menyimpan apa yang menurutnya merupakan implementasi dari tipe dasarnya do, maka Anda memiliki masalah. Jadi, ya, penutupan akan menjadi cara yang lebih baik, aman-warisan untuk menyimpan referensi ke implementasi fungsi asli jika menggunakan metode ini — meskipun itu akan membutuhkan penggunaan Function.prototype.call(this).
binki
16

Saya khawatir teladan Anda tidak bekerja seperti yang Anda pikirkan. Bagian ini:

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

menimpa definisi

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

Karena objek yang baru dibuat sudah memiliki properti "do", itu tidak mencari rantai prototypal.

Bentuk klasik warisan dalam Javascript adalah awkard, dan sulit untuk dipahami. Saya sarankan menggunakan pola pewarisan sederhana Douglas Crockfords sebagai gantinya. Seperti ini:

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

Menurut pendapat saya cara yang jauh lebih jelas dalam menangani objek, konstruktor dan pewarisan dalam javascript. Anda dapat membaca lebih lanjut di Crockfords Javascript: Bagian yang baik .

Magnar
sumber
3
Memang sangat bagus, tetapi Anda tidak dapat benar-benar memanggilnya base_dosebagai fungsi, karena Anda kehilangan thisikatan dengan dometode asli seperti itu. Jadi, pengaturan metode dasar sedikit lebih kompleks, terutama jika Anda ingin menyebutnya menggunakan objek dasar sebagai thisganti objek anak. Saya akan menyarankan sesuatu yang mirip base_do.apply(me, arguments).
Giulio Piancastelli
Hanya bertanya-tanya, kenapa tidak Anda gunakan varuntuk base_do? Apakah ini disengaja dan, jika ya, mengapa? Dan, seperti yang dikatakan @GiulioPiancastelli, apakah pemutusan ini thismengikat di dalam base_do()atau akankah panggilan itu mewarisi thisdari pemanggilnya?
binki
@binki Saya memperbarui contoh. The varharus ada. Menggunakan call(atau apply) memungkinkan kita untuk mengikat thisdengan benar.
Magnar
10

Saya tahu posting ini berasal dari 4 tahun yang lalu, tetapi karena latar belakang C # saya, saya sedang mencari cara untuk memanggil kelas dasar tanpa harus menentukan nama kelas tetapi mendapatkannya dengan properti di subclass. Jadi saya hanya perubahan jawaban Christoph akan

Dari ini:

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

Untuk ini:

this.constructor.prototype.doStuff.call(this /*, args...*/);
lucu
sumber
6
Jangan lakukan ini, this.constructormungkin tidak selalu menunjuk ke sana MyClass.
Bergi
Yah, seharusnya begitu, kecuali seseorang mengacaukan pengaturan warisan. 'ini' harus selalu merujuk ke objek tingkat atas, dan properti 'konstruktornya' harus selalu menunjuk ke fungsi konstruktornya sendiri.
Triynko
5

jika Anda mendefinisikan fungsi seperti ini (menggunakan OOP)

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

ada dua cara untuk memanggil fungsi prototipe: 1) membuat instance dan memanggil fungsi objek:

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

dan cara lainnya adalah ... 2) memanggil fungsi langsung dari prototipe:

Person.prototype.say('hello there!');
Alejandro Silva
sumber
5

Solusi ini menggunakan Object.getPrototypeOf

TestA adalah super yang dimiliki getName

TestBadalah anak yang menimpa getNametetapi, juga memiliki getBothNamespanggilan superversi getNamedan juga childversi

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

Manish Shrotriya
sumber
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();
    };
}
Berezh
sumber
4

Sebuah 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);
}
Anum Malik
sumber
Meskipun mungkin berhasil, saya tidak akan merekomendasikan solusi ini. Ada banyak alasan mengapa penggunaan __proto__(yang mengubah [[Prototipe]] `suatu objek) harus dihindari. Silakan gunakan Object.create(...)rute sebagai gantinya.
KarelG
2

Jika saya mengerti dengan benar, Anda ingin fungsionalitas Base selalu dilakukan, sementara sebagian harus dibiarkan implementasi.

Anda mungkin terbantu oleh pola desain ' metode templat '.

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 */ }
xtofl
sumber
1

Jika Anda tahu kelas super Anda dengan namanya, Anda dapat melakukan sesuatu seperti ini:

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

Ini akan dicetak

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

seperti yang diharapkan.

Michael
sumber
1

Cara lain dengan ES5 adalah dengan secara eksplisit melintasi rantai prototipe menggunakan 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()
Max Fichtelmann
sumber
0

Tidak, Anda perlu memberi fungsi do di konstruktor dan fungsi do di prototipe nama yang berbeda.

ChrisInCambo
sumber
0

Selain itu, jika Anda ingin mengganti semua instance dan bukan hanya satu instance khusus, yang ini mungkin membantu.

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

hasil:

doing original
doing override
supertonsky
sumber
-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();
pengguna4935542
sumber