Metode penggantian JavaScript

87

Katakanlah Anda memiliki kode di bawah ini:

function A() {
    function modify() {
       x = 300;
       y = 400;
    }

    var c = new C();
}

function B() {
    function modify(){
       x = 3000;
       y = 4000;
    }

    var c = new C();
}

C = function () {
   var x = 10;
   var y = 20;

   function modify() {
      x = 30;
      y = 40;
   };

   modify();
   alert("The sum is: " + (x+y));
}

Sekarang pertanyaannya adalah, jika ada cara di mana saya bisa mengganti metode modifydari Cdengan metode yang ada di Adan B. Di Java Anda akan menggunakan super-keyword, tetapi bagaimana Anda bisa mencapai sesuatu seperti ini di JavaScript?

Daniel Nastase
sumber
6
modifybukan metode tetapi fungsi bersarang - ada perbedaan antara keduanya ...
Šime Vidas
2
Di Java, Anda menggunakan superkata kunci untuk mengakses bidang non-privat dan metode kelas-super. Anda tidak menggunakannya untuk menimpanya.
FK82

Jawaban:

138

Sunting: Sekarang enam tahun sejak jawaban asli ditulis dan banyak yang telah berubah!

  • Jika Anda menggunakan versi JavaScript yang lebih baru, mungkin dikompilasi dengan alat seperti Babel , Anda dapat menggunakan kelas yang sebenarnya .
  • Jika Anda menggunakan konstruktor komponen seperti kelas yang disediakan oleh Angular atau React , Anda akan ingin melihat di dokumen untuk kerangka itu.
  • Jika Anda menggunakan ES5 dan membuat kelas "palsu" dengan tangan menggunakan prototipe, jawaban di bawah ini masih sama seperti sebelumnya.

Semoga berhasil!


Warisan JavaScript terlihat sedikit berbeda dari Java. Berikut adalah tampilan sistem objek JavaScript asli:

// Create a class
function Vehicle(color){
  this.color = color;
}

// Add an instance method
Vehicle.prototype.go = function(){
  return "Underway in " + this.color;
}

// Add a second class
function Car(color){
  this.color = color;
}

// And declare it is a subclass of the first
Car.prototype = new Vehicle();

// Override the instance method
Car.prototype.go = function(){
  return Vehicle.prototype.go.call(this) + " car"
}

// Create some instances and see the overridden behavior.
var v = new Vehicle("blue");
v.go() // "Underway in blue"

var c = new Car("red");
c.go() // "Underway in red car"

Sayangnya ini agak jelek dan tidak termasuk cara yang sangat bagus untuk "super": Anda harus secara manual menentukan metode kelas induk mana yang ingin Anda panggil. Hasilnya, ada berbagai alat untuk membuat kelas menjadi lebih baik. Coba lihat Prototype.js, Backbone.js, atau pustaka serupa yang menyertakan sintaks yang lebih baik untuk melakukan OOP di js.

Adam
sumber
2
Alternatifnya untuk menggunakan alat untuk "membuat kelas menjadi lebih baik", Anda tidak dapat membuat kelas sama sekali. Emulasi OO klasik di js selalu menjadi berantakan.
Raynos
1
(bukan komentar yang membangun) karena menjadi bahasa "tingkat rendah" di dunia browser sangat jelek tanpa alasan. Masih mempelajarinya, terima kasih!
dnuske
9
Saya percaya alih-alih Car.prototype = new Vehicle (); ini harus Car.prototype = Object.create (Vehicle.prototype); tidak?
Yordania
@Martin benar, lihat javascript-inheritance
matoni
Salah satu alasan mengapa Car.prototype = new Vehicle (); tidak benar karena tidak memiliki apa pun untuk diteruskan ke Vehicle () saat menerima warna.
Tushar Arora
63

Karena ini adalah hit teratas di Google, saya ingin memberikan jawaban yang diperbarui.

Menggunakan kelas ES6 membuat pewarisan dan penggantian metode jauh lebih mudah:

'use strict';

class A {
    speak() {
        console.log("I'm A");
    }
}

class B extends A {
    speak() {
        super.speak();

        console.log("I'm B");
    }
}

var a = new A();
a.speak();
// Output:
// I'm A

var b = new B();
b.speak();
// Output:
// I'm A
// I'm B

Kata superkunci mengacu pada kelas induk saat digunakan dalam kelas pewarisan. Selain itu, semua metode di kelas induk terikat ke instance anak, jadi Anda tidak perlu menulis super.method.apply(this);.

Mengenai kompatibilitas: tabel kompatibilitas ES6 hanya menampilkan versi terbaru dari kelas dukungan pemain utama (kebanyakan). Browser V8 telah memilikinya sejak Januari tahun ini (Chrome dan Opera), dan Firefox, menggunakan mesin SpiderMonkey JS, akan melihat kelas bulan depan dengan rilis resmi Firefox 45 mereka. Di sisi seluler, Android masih belum mendukung fitur ini, sedangkan iOS 9 yang rilis lima bulan lalu, memiliki dukungan parsial.

Untungnya, ada Babel , pustaka JS untuk mengompilasi ulang kode Harmony menjadi kode ES5. Classes, dan banyak fitur keren lainnya di ES6 dapat membuat kode Javascript Anda lebih mudah dibaca dan dipelihara.

Yotam Ofek
sumber
10
Jawaban ini membutuhkan lebih banyak pujian dan saat ini merupakan jawaban yang benar.
Klompenrunner
6

Once harus menghindari meniru OO klasik dan sebagai gantinya gunakan OO prototipe. Pustaka utilitas yang bagus untuk prototipe OO adalah ciri .

Daripada menimpa metode dan menyiapkan rantai pewarisan (seseorang harus selalu menyukai komposisi objek daripada pewarisan objek), Anda harus menggabungkan fungsi yang dapat digunakan kembali menjadi ciri-ciri dan membuat objek dengannya.

Contoh Langsung

var modifyA = {
    modify: function() {
        this.x = 300;
        this.y = 400;
    }
};

var modifyB = {
    modify: function() {
        this.x = 3000;
        this.y = 4000;
    }
};

C = function(trait) {
    var o = Object.create(Object.prototype, Trait(trait));

    o.modify();
    console.log("sum : " + (o.x + o.y));

    return o;
}

//C(modifyA);
C(modifyB);
Raynos
sumber
10
Anda tidak menjawab pertanyaan itu. Ini harus menjadi komentar jika ada.
FK82
@ FK82 mari kita lanjutkan diskusi ini dalam obrolan
Raynos
3

modifikasikan () dalam contoh Anda adalah fungsi pribadi, yang tidak akan dapat diakses dari mana pun kecuali dalam definisi A, B, atau C Anda. Anda harus mendeklarasikannya sebagai

this.modify = function(){}

C tidak memiliki referensi ke orang tuanya, kecuali Anda meneruskannya ke C. Jika C diatur untuk mewarisi dari A atau B, C akan mewarisi metode publiknya (bukan fungsi pribadinya seperti yang telah Anda modifikasi () tentukan). Setelah C mewarisi metode dari induknya, Anda dapat mengganti metode yang diwariskan.

Alex Heyd
sumber
memodifikasi adalah fungsi lokal. Tidak ada pribadi di javascript
Raynos
local / private, bukankah itu sama, hanya istilah yang berbeda?
Alex Heyd
2

metode modify()yang Anda panggil terakhir kali dipanggil dalam konteks global jika Anda ingin menimpanya, modify()Anda harus mewarisi Aatau B.

Mungkin Anda mencoba melakukan ini:

Dalam hal ini CmewarisiA

function A() {
    this.modify = function() {
        alert("in A");
    }
}

function B() {
    this.modify = function() {
        alert("in B");
    }
}

C = function() {
    this.modify = function() {
        alert("in C");
    };

    C.prototype.modify(); // you can call this method where you need to call modify of the parent class
}

C.prototype = new A();
lovesh
sumber
1
C.prototype.modify()akan memiliki nilai yang salah this. Yaitu C.prototypesebagai pengganti contoh c. Silakan gunakan .call(this)tapi kemudian jawaban Anda hanya duplikat :)
Raynos
1

Tidak, kecuali jika Anda membuat semua variabel "publik", yaitu menjadikannya anggota Function baik secara langsung atau melalui prototypeproperti.

var C = function( ) {
    this.x = 10 , this.y = 20 ;
    this.modify = function( ) {
        this.x = 30 , this.y = 40 ;
        console.log("(!) C >> " + (this.x + this.y) ) ;
    } ;
} ;

var A = function( ) {
    this.modify = function( ) {
       this.x = 300 , this.y = 400 ;
       console.log("(!) A >> " + (this.x + this.y) ) ;
    } ;
} ;
    A.prototype = new C ;

var B = function( ) {
    this.modify = function( ) {
       this.x = 3000 , this.y = 4000 ;
       console.log("(!) B >> " + (this.x + this.y) ) ;
    } ;
} ;


new C( ).modify( ) ;
new A( ).modify( ) ;
new B( ).modify( ) ; 

Anda akan melihat beberapa perubahan.

Yang terpenting, panggilan ke konstruktor "kelas super" yang seharusnya sekarang tersirat dalam baris ini:

<name>.prototype = new C ;

Kedua Adan Bsekarang akan memiliki dimodifikasi secara individu anggota xdany yang tidak akan terjadi jika kita akan menulis ... = Csebagai gantinya.

Kemudian, x, ydan modifysemua "publik" anggota agar menetapkan berbeda Functionuntuk mereka

 <name>.prototype.modify = function( ) { /* ... */ }

akan "menimpa" yang asli Function dengan nama itu.

Terakhir, panggilan ke modifytidak bisa dilakukan diFunction deklarasi karena panggilan implisit ke "super-class" kemudian akan dijalankan lagi saat kita menetapkan "super-class" prototypeyang seharusnya ke properti "sub-class" yang seharusnya.

Tapi begini, kurang lebih begini cara Anda melakukan hal semacam ini di JavaScript.

HTH,

FK

FK82
sumber
Tidak ada gunanya <name>.prototype = new C;membayangi semua anggota prototipe
Raynos
@Raynos: Ya, ada. Intinya, semua yang mewarisi Objectsakan berbagi anggota yang sama Cjika Anda tidak membuat instance CObject. Dengan demikian, perubahan xdalam Aakan berubah xdi Cdan dengan demikian mengubah xdi B. Yang jelas tidak diinginkan.
FK82
Anda melewatkan intinya. Anda dapat menghapus baris dan kode tersebut akan tetap berfungsi
Raynos
@Raynos: Saya khawatir Anda kehilangan poin Anda sendiri. ;-) Kami ingin Adan Bmewarisi dari C. Jika garis itu hilang, itu tidak akan menjadi masalah. Pada kenyataannya, satu-satunya prototipe Adan juga B"bayangan" akan memiliki akses ke dalam kasus itu Object.prototype.
FK82
lihat kodenya. A dan B tidak menggunakan salah satu anggota C hingga prototipe. Jadi "mewarisi" dari C tidak ada gunanya. Hal ini karena A dan B mendefinisikan kembali x, ydan modifysehingga bayangan semua Canggota 's. Apa gunanya meletakkan C dalam prototipe jika Anda tidak menggunakannya? Itu kode mati.
Raynos
0

function A() {
    var c = new C();
	c.modify = function(){
		c.x = 123;
		c.y = 333;
	}
	c.sum();
}

function B() {
    var c = new C();
	c.modify = function(){
		c.x = 999;
		c.y = 333;
	}
	c.sum();
}


C = function () {
   this.x = 10;
   this.y = 20;

   this.modify = function() {
      this.x = 30;
      this.y = 40;
   };
   
   this.sum = function(){
	this.modify();
	console.log("The sum is: " + (this.x+this.y));
   }
}

A();
B();

nghien_net 89
sumber