javascript'te klasik kalıtım ve prototip kalıtım karşılaştırması


118

Google'da çok fazla bağlantı kurdum ve klasik kalıtım ile prototip kalıtım arasındaki fark hakkında iyi bir fikir alamıyorum?

Bunlardan bazı şeyler öğrendim ama kavramlar konusunda hala kafam karışık.

Klasik miras

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

Klasik kalıtım içinde prototip kalıtımı kullanıyor mu?

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

Yukarıdaki bağlantıdan, klasik kalıtımda çalışma zamanında yeni yöntemler ekleyemeyeceğimizi öğrendim . Bu doğru mu? Ancak yukarıdaki kodu kontrol edebilirsiniz, "taşıma" yöntemini ve herhangi bir yöntemi prototip üzerinden çalışma zamanında ekleyebilirim . Yani bu prototip tabanlı klasik kalıtım mı? Eğer öyleyse, gerçek klasik miras ve prototip kalıtımı nedir? Kafam karıştı.

Prototypal kalıtımı.

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

Bu klasik mirasa benzer mi? Prototip kalıtımın ne olduğu konusunda kafam tamamen karıştı mı? Klasik miras nedir? Klasik kalıtım neden kötüdür?

Bunları basit bir şekilde daha iyi anlamak için bana basit bir örnek verebilir misiniz?

Teşekkürler,

Siva


Çoğaltın,
şuna bakın

5
Burada ne anlattığınızdan emin değilim - ilk kod bloğu klasik değil , prototip kalıtımdır. İkinci kod bloğunuzun hiç mirası yoktur!
Alnitak


@alnitak developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… bu bağlantı, birinin klasik miras olduğunu söyler. bu yüzden kafam karıştı.
SivaRajini

Neden klasik kalıtımdan kaçınmak isteyebileceğinizle ilgili daha fazla bilgi için, "Classical Inheritance is Exsolete: How to Think in Prototypal OO" vimeo.com/69255635
Eric Elliott

Yanıtlar:


248

Sorunuzda gösterdiğiniz her iki kod örneği de prototip kalıtımdan yararlanır. Aslında JavaScript'te yazdığınız herhangi bir nesne yönelimli kod, prototip kalıtımın bir paradigmasıdır. JavaScript'in klasik kalıtımı yoktur. Bu işleri biraz düzeltmeli:

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

Gördüğünüz gibi prototip kalıtım ve klasik kalıtım, iki farklı kalıtım paradigmasıdır. Self, Lua ve JavaScript gibi bazı diller prototip kalıtımı destekler. Ancak C ++, Java ve C # gibi çoğu dil klasik kalıtımı destekler.


Nesne Tabanlı Programlamaya Hızlı Bir Bakış

Hem prototip kalıtım hem de klasik kalıtım, nesne yönelimli programlama paradigmalarıdır (yani nesnelerle ilgilenirler). Nesneler, gerçek dünya varlığının özelliklerini kapsayan basit soyutlamalardır (yani, programdaki gerçek sözcükleri temsil ederler). Bu soyutlama olarak bilinir.

Soyutlama: Bilgisayar programlarında gerçek dünyadaki şeylerin temsili.

Teorik olarak bir soyutlama, "belirli örneklerden ortak özelliklerin çıkarılmasıyla oluşturulan genel bir kavram" olarak tanımlanır. Ancak bu açıklama uğruna bunun yerine yukarıda belirtilen tanımı kullanacağız.

Şimdi bazı nesnelerin birçok ortak noktası var. Örneğin bir çamur bisikleti ve bir Harley Davidson birçok ortak noktaya sahiptir.

Çamur bisikleti:

Çamur bisikleti.

Bir Harley Davidson:

Bir Harley Davidson

Çamur bisikleti ve Harley Davidson ikisi de bisiklet. Dolayısıyla bisiklet, hem çamur bisikletinin hem de Harley Davidson'un bir genellemesidir.

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

Yukarıdaki örnekte bisiklet, çamur bisikleti ve Harley Davidson hepsi soyutlamalardır. Ancak bisiklet, çamur bisikletinin ve Harley Davidson'un daha genel bir soyutlamasıdır (yani hem çamur bisikleti hem de Harley Davidson belirli bisiklet türleridir).

Genelleme: Daha spesifik bir soyutlamanın bir soyutlaması.

Nesne yönelimli programlamada nesneler yaratırız (gerçek dünya varlıklarının soyutlamalarıdır) ve bu nesnelerin genellemelerini oluşturmak için sınıfları veya prototipleri kullanırız. Genellemeler miras yoluyla oluşturulur. Bisiklet, çamur bisikletinin bir genellemesidir. Bu nedenle çamur bisikletleri bisikletlerden miras alınır.


Klasik Nesne Tabanlı Programlama

Klasik nesne yönelimli programlamada iki tür soyutlamaya sahibiz: sınıflar ve nesneler. Daha önce belirtildiği gibi bir nesne, gerçek dünya varlığının bir soyutlamasıdır. Öte yandan bir sınıf, bir nesnenin veya başka bir sınıfın soyutlamasıdır (yani bir genellemedir). Örneğin şunları göz önünde bulundurun:

+----------------------+----------------+---------------------------------------+
| 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.              |
+----------------------+----------------+---------------------------------------+

Klasik nesne yönelimli programlama dillerinde görebileceğiniz gibi nesneler yalnızca soyutlamalardır (yani tüm nesnelerin soyutlama düzeyi 1'dir) ve sınıflar yalnızca genellemedir (yani, tüm sınıfların 1'den büyük bir soyutlama düzeyi vardır).

Klasik nesne yönelimli programlama dillerindeki nesneler yalnızca sınıflar başlatılarak oluşturulabilir:

class Human {
    // ...
}

class Man extends Human {
    // ...
}

Man johnDoe = new Man();

Klasik nesne yönelimli programlama dillerinde özetle, nesneler gerçek dünya varlıklarının soyutlamalarıdır ve sınıflar genelleştirmelerdir (yani nesnelerin veya diğer sınıfların soyutlamaları).

Dolayısıyla, soyutlama düzeyi arttıkça varlıklar daha genel hale gelir ve soyutlama düzeyi azaldıkça varlıklar daha spesifik hale gelir. Bu anlamda, soyutlama seviyesi, daha spesifik varlıklardan daha genel varlıklara kadar değişen bir ölçeğe benzer.


Prototypal Nesne Tabanlı Programlama

Prototip nesne yönelimli programlama dilleri, klasik nesne yönelimli programlama dillerinden çok daha basittir, çünkü prototip nesne yönelimli programlamada yalnızca bir tür soyutlamaya sahibiz (yani nesneler). Örneğin şunları göz önünde bulundurun:

+----------------------+----------------+---------------------------------------+
| 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.              |
+----------------------+----------------+---------------------------------------+

Prototip nesne yönelimli programlama dillerinde görebileceğiniz gibi nesneler, gerçek dünya varlıklarının (bu durumda basitçe nesne olarak adlandırılır) veya diğer nesnelerin (bu durumda soyutladıkları nesnelerin prototipleri olarak adlandırılırlar) soyutlamalarıdır. Dolayısıyla bir prototip bir genellemedir.

Prototip nesne yönelimli programlama dillerindeki nesneler, ya ex-nihilo (yani yoktan) ya da başka bir nesneden (yeni oluşturulan nesnenin prototipi haline gelir) oluşturulabilir:

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

Benim mütevazı görüşüme göre prototip nesne yönelimli programlama dilleri, klasik nesne yönelimli programlama dillerinden daha güçlüdür çünkü:

  1. Yalnızca bir tür soyutlama vardır.
  2. Genellemeler basit nesnelerdir.

Şimdiye kadar klasik kalıtım ile prototip kalıtım arasındaki farkı anlamış olmalısınız. Klasik miras, diğer sınıflardan miras alan sınıflarla sınırlıdır. Bununla birlikte, prototip kalıtımı yalnızca diğer prototiplerden miras alınan prototipleri değil, aynı zamanda prototiplerden miras alan nesneleri de içerir.


Prototip Sınıfı İzomorfizm

Prototiplerin ve sınıfların çok benzer olduğunu fark etmiş olmalısınız. Bu doğru. Onlar. Aslında o kadar benzerler ki, sınıfları modellemek için prototipleri gerçekten kullanabilirsiniz:

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;
    }
}

Yukarıdaki CLASSişlevi kullanarak, sınıflara benzeyen prototipler oluşturabilirsiniz:

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

Ancak bunun tersi doğru değildir (yani, prototipleri modellemek için sınıfları kullanamazsınız). Bunun nedeni, prototiplerin nesne olması, ancak sınıfların nesne olmamasıdır. Bunlar tamamen farklı bir soyutlama türüdür.


Sonuç

Özetle, bir soyutlamanın "belirli örneklerden ortak özelliklerin çıkarılmasıyla oluşturulan genel bir kavram" olduğunu ve genellemenin "daha spesifik bir soyutlamanın soyutlaması" olduğunu öğrendik . Ayrıca prototip ve klasik kalıtım arasındaki farkları ve her ikisinin de aynı madalyonun iki yüzü olduğunu öğrendik.

Ayrılık notunda, iki prototip kalıtım modeli olduğunu belirtmek isterim: prototip kalıbı ve yapıcı modeli. Prototip model, prototip kalıtımın kanonik modelidir, oysa yapıcı modeli, prototip kalıtımın daha çok klasik kalıtım gibi görünmesini sağlamak için kullanılır. Şahsen ben prototip kalıbı tercih ederim.

Not: " Prototypal Kalıtım Neden Önemlidir " adlı blog yazısını yazan ve " Prototip kalıtımın klasiklere göre faydaları? " Sorusunu yanıtlayan kişiyim . Cevabım kabul edilen cevaptır.


2
harika cevabın için teşekkürler prototip modelin yapıcı modeline kıyasla nasıl daha iyi olduğunu anlamam gerekiyor. herhangi bir örnek?
SivaRajini

1
Blogumda yapıcılar ve prototipler hakkında karşılaştırmalı bir eleştiri yazdım: aaditmshah.github.io/why-prototypal-inheritance-matters/…
Aadit M Shah

Öyleyse, kalıtım elde etmek için javascript'teki işlevleri kullandığımızda, bir şekilde klasik kalıtım modelini kullandığımızı ve düz nesneler kullandığımızda prototip kalıtımın (her ikisi de dahili olarak prototip kalıtımın ardından) olduğunu söylemek doğru olur mu?
Swanidhi

1
@Swanidhi Hayır. JavaScript kullanıyorsanız prototip kalıtım modelini kullanıyorsunuz demektir. Bununla birlikte, JavaScript'in prototip kalıtımının iki çeşidi vardır: işlevleri kullanmak (yani yapıcı kalıbı) ve nesneleri kullanmak (yani prototip kalıbı).
Aadit M Shah

5
@Swanidhi Hayır. Hala prototip. JavaScript'in "sınıfları" yoktur ve bu nedenle, yapıcılar dahil, klasik JavaScript'te kesinlikle hiçbir şey yoktur. Hala prototip miras. İnsanların klasik mirasla karıştırdığı tuhaf bir prototip miras biçimi. Kısaca, 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. Umarım bu işleri açıklığa kavuşturur.
Aadit M Shah

8

Kalıtıma geçmeden önce , javascript'te örnekler (nesneler) oluşturmak için iki ana modele göz atacağız :

Klasik model: Nesne bir plandan (sınıf) oluşturulur

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

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

Prototypal modeli: Nesne doğrudan başka bir nesneden oluşturulur.

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

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

Her iki durumda da, Devralma *, nesnelerin prototip nesnesi kullanılarak bağlanmasıyla elde edilir.

(* temel sınıf yöntemlerine prototip nesnesi aracılığıyla. türetilmiş sınıf aracılığıyla erişilebilir ve türetilmiş sınıfta açıkça mevcut olmaları gerekmez.)

İşte daha iyi anlamak için iyi bir açıklama ( http://www.objectplayground.com/ )


0

Köpek bir hayvandır. Suzanna bir köpektir. Klasik kalıtımda, Animalbir sınıftır, Dogbir alt sınıfıdır Animalve suzannabir Dog.

Prototip kalıtımda sınıf yoktur. animalBir nesneye sahipsin . A dog, klonlayan ve genişleten animal(prototip nesnesi) başka bir nesnedir . suzannakopyalayan ve genişleyen üçüncü bir nesnedir 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();

DogBunun yerine yazarsanız dog, özellikle Dogbir tür "yapıcı" işlevi yaparsanız, prototip kalıtım yapmıyorsunuzdur; (sözde) klasik kalıtım yapıyorsunuz . Bunu Object.create()başarmak için kullanıyor olmanız, prototip kalıtım yaptığınız anlamına gelmez.

Aslında, JavaScript yalnızca prototip mirasını destekler. Kafa karıştırıcı newoperatör ve .prototypeöznitelik, prototip kalıtımın (sözde) klasik kalıtım gibi görünmesini sağlamak için oradadır.

Douglas Crockford, "JavaScript: The Good Parts" adlı kitabında bunu ayrıntılı olarak araştırıyor.

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.