warisan klasik vs warisan prototipe dalam javascript

118

Saya telah mencari di Google begitu banyak tautan dan tidak dapat memahami perbedaan antara pewarisan klasik dan pewarisan prototipe?

Saya telah belajar beberapa hal dari ini tetapi saya masih bingung tentang konsepnya.

Warisan klasik

// Shape - superclass
function Shape() {
  this.x = 0;
  this.y = 0;
}

//superclass method
Shape.prototype.move = function(x, y) {
    this.x += x;
    this.y += y;
    console.info("Shape moved.");
};

// Rectangle - subclass
function Rectangle() {
  Shape.call(this); //call super constructor.
}

//subclass extends superclass
Rectangle.prototype = Object.create(Shape.prototype);

Apakah warisan klasik menggunakan warisan prototipe di dalamnya?

http://aaditmshah.github.io/why-prototypal-inheritance-matters/

Dari tautan di atas, saya belajar bahwa kita tidak dapat menambahkan metode baru pada waktu proses dalam pewarisan klasik . Apakah ini benar? Tetapi Anda dapat memeriksa kode di atas, saya dapat menambahkan metode "bergerak" dan metode apa pun pada saat dijalankan melalui prototipe . Jadi ini warisan klasik berbasis prototipe? Jika demikian, apa sebenarnya warisan klasik dan warisan prototipe? Saya bingung tentang itu.

Pewarisan prototipe.

function Circle(radius) {
    this.radius = radius;
}
Circle.prototype.area = function () {
    var radius = this.radius;
    return Math.PI * radius * radius;
};
Circle.prototype.circumference: function () {
    return 2 * Math.PI * this.radius;
};
var circle = new Circle(5);
var circle2 = new Circle(10);

Apakah ini mirip dengan warisan klasik? Saya benar-benar bingung tentang apa itu warisan prototipe? Apa itu warisan klasik? Mengapa warisan klasik buruk?

Bisakah Anda memberi saya contoh sederhana untuk lebih memahami ini dengan cara yang sederhana.

Terima kasih,

Siva

SivaRajini
sumber
Gandakan, lihat ini: stackoverflow.com/questions/1595611/…
Silviu Burcea
5
tidak yakin apa yang Anda maksud di sini - blok kode pertama adalah warisan prototipe, bukan klasik. Blok kode kedua Anda tidak memiliki warisan sama sekali!
Alnitak
Ini mungkin xplain: blog.stephenwyattbush.com/2012/05/01/…
HasanAboShally
@alnitak developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… tautan ini memberitahukan bahwa salah satunya adalah warisan klasik. Itu sebabnya saya bingung.
SivaRajini
Untuk lebih lanjut tentang mengapa Anda mungkin ingin menghindari warisan klasik, lihat ceramah saya, "Warisan Klasik Usang: Bagaimana Berpikir dalam Prototypal OO" vimeo.com/69255635
Eric Elliott

Jawaban:

248

Kedua contoh kode yang Anda tunjukkan dalam pertanyaan Anda menggunakan pewarisan prototipe. Faktanya, kode berorientasi objek apa pun yang Anda tulis di JavaScript adalah paradigma pewarisan prototipe. JavaScript tidak memiliki warisan klasik. Ini harus menjelaskan sedikit:

                                   Inheritance
                                        |
                         +-----------------------------+
                         |                             |
                         v                             v
                    Prototypal                     Classical
                         |
         +------------------------------+
         |                              |
         v                              v
Prototypal Pattern             Constructor Pattern

Seperti yang Anda lihat, pewarisan prototipe dan pewarisan klasik adalah dua paradigma pewarisan yang berbeda. Beberapa bahasa seperti Self, Lua dan JavaScript mendukung pewarisan prototipe. Namun kebanyakan bahasa seperti C ++, Java dan C # mendukung pewarisan klasik.


Tinjauan Singkat Pemrograman Berorientasi Objek

Baik pewarisan prototipe dan pewarisan klasik adalah paradigma pemrograman berorientasi objek (yaitu, mereka berurusan dengan objek). Objek hanyalah abstraksi yang merangkum properti dari entitas dunia nyata (yaitu mereka mewakili hal-hal nyata dalam program). Ini dikenal sebagai abstraksi.

Abstraksi: Representasi hal-hal dunia nyata dalam program komputer.

Secara teoritis abstraksi didefinisikan sebagai "konsep umum yang dibentuk dengan mengekstraksi fitur umum dari contoh tertentu". Namun demi penjelasan ini, kami akan menggunakan definisi yang disebutkan di atas.

Sekarang beberapa objek memiliki banyak kesamaan. Misalnya sepeda lumpur dan Harley Davidson memiliki banyak kesamaan.

Sepeda lumpur:

Sepeda lumpur.

Harley Davidson:

Harley Davidson

Sepeda lumpur dan Harley Davidson keduanya adalah sepeda. Karenanya sepeda adalah generalisasi dari sepeda lumpur dan Harley Davidson.

                   Bike
                     |
    +---------------------------------+
    |                                 |
    v                                 v
Mud Bike                       Harley Davidson

Dalam contoh di atas, sepeda, sepeda lumpur, dan Harley Davidson, semuanya adalah abstraksi. Namun sepeda motor adalah abstraksi yang lebih umum dari sepeda lumpur dan Harley Davidson (yaitu sepeda lumpur dan Harley Davidson adalah jenis sepeda tertentu).

Generalisasi: Abstraksi dari abstraksi yang lebih spesifik.

Dalam pemrograman berorientasi objek kami membuat objek (yang merupakan abstraksi entitas dunia nyata) dan kami menggunakan kelas atau prototipe untuk membuat generalisasi objek ini. Generalisasi dibuat melalui pewarisan. Sepeda adalah generalisasi sepeda lumpur. Karenanya sepeda lumpur diwarisi dari sepeda.


Pemrograman Berorientasi Objek Klasik

Dalam pemrograman berorientasi objek klasik kita memiliki dua jenis abstraksi: kelas dan objek. Sebuah objek, seperti yang disebutkan sebelumnya, adalah abstraksi dari entitas dunia nyata. Kelas di sisi lain adalah abstraksi dari suatu objek atau kelas lain (yaitu generalisasi). Misalnya, pertimbangkan:

+----------------------+----------------+---------------------------------------+
| Level of Abstraction | Name of Entity |                Comments               |
+----------------------+----------------+---------------------------------------+
| 0                    | John Doe       | Real World Entity.                    |
| 1                    | johnDoe        | Variable holding object.              |
| 2                    | Man            | Class of object johnDoe.              |
| 3                    | Human          | Superclass of class Man.              |
+----------------------+----------------+---------------------------------------+

Seperti yang Anda lihat dalam bahasa pemrograman berorientasi objek klasik, objek hanyalah abstraksi (yaitu semua objek memiliki tingkat abstraksi 1) dan kelas hanya generalisasi (yaitu semua kelas memiliki tingkat abstraksi lebih besar dari 1).

Objek dalam bahasa pemrograman berorientasi objek klasik hanya dapat dibuat dengan membuat instance kelas:

class Human {
    // ...
}

class Man extends Human {
    // ...
}

Man johnDoe = new Man();

Dalam penjumlahan dalam bahasa pemrograman berorientasi objek klasik, objek adalah abstraksi dari entitas dunia nyata dan kelas adalah generalisasi (yaitu abstraksi dari objek atau kelas lain).

Oleh karena itu, ketika tingkat abstraksi meningkat, entitas menjadi lebih umum dan ketika tingkat abstraksi menurun, entitas menjadi lebih spesifik. Dalam pengertian ini, tingkat abstraksi dapat dianalogikan dengan skala yang berkisar dari entitas yang lebih spesifik hingga entitas yang lebih umum.


Pemrograman Berorientasi Objek Prototipe

Bahasa pemrograman berorientasi objek prototypal jauh lebih sederhana daripada bahasa pemrograman berorientasi objek klasik karena dalam pemrograman berorientasi objek prototypal kita hanya memiliki satu jenis abstraksi (yaitu objek). Misalnya, pertimbangkan:

+----------------------+----------------+---------------------------------------+
| Level of Abstraction | Name of Entity |                Comments               |
+----------------------+----------------+---------------------------------------+
| 0                    | John Doe       | Real World Entity.                    |
| 1                    | johnDoe        | Variable holding object.              |
| 2                    | man            | Prototype of object johnDoe.          |
| 3                    | human          | Prototype of object man.              |
+----------------------+----------------+---------------------------------------+

Seperti yang Anda lihat dalam bahasa pemrograman berorientasi objek prototipe objek adalah abstraksi dari entitas dunia nyata (dalam hal ini mereka hanya disebut objek) atau objek lain (dalam hal ini mereka disebut prototipe objek yang mereka abstrak). Karenanya prototipe adalah generalisasi.

Objek dalam prototipe bahasa pemrograman berorientasi objek dapat dibuat baik ex-nihilo (yaitu dari ketiadaan) atau dari objek lain (yang menjadi prototipe dari objek yang baru dibuat):

var human = {};
var man = Object.create(human);
var johnDoe = Object.create(man);

Menurut pendapat saya, bahasa pemrograman berorientasi objek prototipe lebih kuat daripada bahasa pemrograman berorientasi objek klasik karena:

  1. Hanya ada satu jenis abstraksi.
  2. Generalisasi hanyalah objek.

Sekarang Anda pasti sudah menyadari perbedaan antara warisan klasik dan warisan prototipe. Pewarisan klasik terbatas pada kelas yang diturunkan dari kelas lain. Namun pewarisan prototipe tidak hanya mencakup prototipe yang diwarisi dari prototipe lain tetapi juga objek yang diwarisi dari prototipe.


Isomorfisme Kelas Prototipe

Anda pasti telah memperhatikan bahwa prototipe dan kelas sangat mirip. Itu benar. Mereka. Faktanya mereka sangat mirip sehingga Anda benar-benar dapat menggunakan prototipe untuk memodelkan kelas:

function CLASS(base, body) {
    if (arguments.length < 2) body = base, base = Object.prototype;
    var prototype = Object.create(base, {new: {value: create}});
    return body.call(prototype, base), prototype;

    function create() {
        var self = Object.create(prototype);
        return prototype.hasOwnProperty("constructor") &&
            prototype.constructor.apply(self, arguments), self;
    }
}

Dengan menggunakan CLASSfungsi di atas, Anda dapat membuat prototipe yang terlihat seperti kelas:

var Human = CLASS(function () {
    var milliseconds = 1
      , seconds      = 1000 * milliseconds
      , minutes      = 60 * seconds
      , hours        = 60 * minutes
      , days         = 24 * hours
      , years        = 365.2425 * days;

    this.constructor = function (name, sex, dob) {
        this.name = name;
        this.sex = sex;
        this.dob = dob;
    };

    this.age = function () {
        return Math.floor((new Date - this.dob) / years);
    };
});

var Man = CLASS(Human, function (Human) {
    this.constructor = function (name, dob) {
        Human.constructor.call(this, name, "male", dob);
        if (this.age() < 18) throw new Error(name + " is a boy, not a man!");
    };
});

var johnDoe = Man.new("John Doe", new Date(1970, 0, 1));

Namun kebalikannya tidak benar (yaitu Anda tidak dapat menggunakan kelas untuk memodelkan prototipe). Ini karena prototipe adalah objek tetapi kelas bukan objek. Mereka adalah jenis abstraksi yang sama sekali berbeda.


Kesimpulan

Kesimpulannya, kita belajar bahwa abstraksi adalah "konsep umum yang dibentuk dengan mengekstraksi fitur umum dari contoh tertentu" dan generalisasi itu adalah "abstraksi dari abstraksi yang lebih spesifik" . Kami juga belajar tentang perbedaan antara warisan prototipe dan klasik dan bagaimana keduanya adalah dua sisi dari koin yang sama.

Pada catatan perpisahan saya ingin menyampaikan bahwa ada dua pola warisan prototipe: pola prototipe dan pola konstruktor. Pola prototypal adalah pola pewarisan prototipal sedangkan pola konstruktor digunakan untuk membuat pewarisan prototip lebih mirip dengan pewarisan klasik. Secara pribadi saya lebih suka pola prototipe.

PS Saya orang yang menulis posting blog " Why Prototypal Inheritance Matters " dan menjawab pertanyaan " Manfaat warisan prototypal daripada klasik? ". Jawaban saya adalah jawaban yang diterima.

Aadit M Shah
sumber
2
terima kasih atas jawaban Anda yang luar biasa. saya perlu memahami bagaimana pola prototipe lebih baik dibandingkan dengan contoh pattern.any konstruktor?
SivaRajini
1
Saya telah menulis kritik komparatif tentang konstruktor vs prototipe di blog saya: aaditmshah.github.io/why-prototypal-inheritance-matters/…
Aadit M Shah
Jadi, apakah benar untuk mengatakan bahwa ketika kita menggunakan fungsi dalam javascript untuk mencapai pewarisan, kita agak menggunakan model pewarisan klasik dan ketika kita menggunakan objek biasa itu pewarisan prototipe (keduanya secara internal mengikuti pewarisan prototipe)?
Swanidhi
1
@Swanidhi Tidak. Jika Anda menggunakan JavaScript, maka Anda menggunakan model pewarisan prototipe. Namun, JavaScript memiliki dua jenis pewarisan prototipe: menggunakan fungsi (yaitu pola konstruktor) dan menggunakan objek (yaitu pola prototipe).
Aadit M Shah
5
@Swanidhi Tidak. Ini masih prototipe. JavaScript tidak memiliki "kelas" dan karenanya sama sekali tidak ada dalam JavaScript di klasik, termasuk konstruktor. Itu masih warisan prototipe. Hanya bentuk warisan prototip yang aneh yang membingungkan orang dengan warisan klasik. Singkatnya, programming with classes = classical inheritance, programming with prototypes = prototypal inheritance, programming with constructors = weird form of prototypal inheritance that looks a lot like classical inheritance. Harapan yang menjelaskan banyak hal.
Aadit M Shah
8

Sebelum beralih ke warisan, kita akan melihat dua model utama untuk membuat contoh (objek) dalam javascript:

Model klasik: Objek dibuat dari cetak biru (kelas)

class Person {
  fn() {...}
} // or constructor function say, function Person() {}

// create instance
let person = new Person();

Model prototipe: Objek dibuat langsung dari objek lain.

// base object
let Person = { fn(){...} }

// instance
let person = Object.create(Person);

Dalam kedua kasus, Warisan * dicapai dengan menautkan objek menggunakan objek prototipe.

(* metode kelas dasar dapat diakses melalui. kelas turunan melalui objek prototipe dan tidak harus secara eksplisit ada di kelas turunan.)

Berikut adalah penjelasan yang bagus untuk lebih memahami ( http://www.objectplayground.com/ )

everlasto
sumber
0

Seekor anjing adalah binatang. Suzanna adalah seekor anjing. Dalam pewarisan klasik, Animaladalah kelas, Dogmerupakan subkelas dari Animal, dan suzannamerupakan turunan dari a Dog.

Dalam pewarisan prototipe, tidak ada kelas. Anda memiliki animal, yang merupakan objek. A dogadalah objek lain, yang mengkloning dan memperluas animal(objek prototipe). suzannaadalah objek ketiga, yang menyalin dan memperluas dog.

let animal = {hasChlorophyl: false};

let dog = Object.create(animal);
Object.assign(dog, {
  speak() {
    console.log("Woof!");
  }
});

let suzanna = Object.create(dog);
Object.assign(suzanna, {
  name: "Suzanna"
});

suzanna.speak();

Jika Anda menulis Dogalih-alih dog, terutama jika Anda membuat Dogsemacam fungsi "konstruktor", maka Anda tidak melakukan pewarisan prototipe; Anda melakukan warisan klasik (palsu) . Fakta bahwa Anda menggunakan Object.create()untuk mencapai ini tidak berarti Anda melakukan pewarisan prototipe.

Faktanya, JavaScript hanya mendukung pewarisan prototipe. newOperator dan .prototypeatribut yang membingungkan ada untuk membuat pewarisan prototipe terlihat seperti pewarisan klasik (pseudo-).

Douglas Crockford mengeksplorasi ini panjang lebar dalam bukunya, "JavaScript: The Good Parts".

Antonis Christofides
sumber