Javascript ne zaman prototipler kullanılır?


93

Js'de prototip yöntemlerini kullanmanın ne zaman uygun olduğunu anlamak istiyorum. Her zaman kullanılmalı mı? Yoksa bunları kullanmanın tercih edilmediği ve / veya performans cezası gerektiren durumlar var mı?

Bu sitede js'de ad aralığı için yaygın yöntemler araştırılırken, çoğu prototip tabanlı olmayan bir uygulama kullanıyor gibi görünüyor: sadece bir ad alanını kapsüllemek için bir nesne veya bir işlev nesnesi kullanmak.

Sınıf temelli bir dilden gelince, paralellikler kurmaya çalışmamak ve prototiplerin "sınıflar" gibi olduğunu ve bahsettiğim ad alanı uygulamalarının statik yöntemler gibi olduğunu düşünmemek zor.

Yanıtlar:


135

Prototipler bir optimizasyondur .

Bunları iyi kullanmanın harika bir örneği jQuery kitaplığıdır. Kullanarak bir jQuery nesnesi elde ettiğiniz her seferinde $('.someClass'), bu nesnenin düzinelerce "yöntemi" vardır. Kütüphane bunu bir nesneyi döndürerek başarabilir:

return {
   show: function() { ... },
   hide: function() { ... },
   css: function() { ... },
   animate: function() { ... },
   // etc...
};

Ancak bu, bellekteki her jQuery nesnesinin aynı yöntemleri içeren düzinelerce adlandırılmış yuvaya sahip olacağı anlamına gelir.

Bunun yerine, bu yöntemler bir prototip üzerinde tanımlanır ve tüm jQuery nesneleri, tüm bu yöntemleri çok düşük çalışma zamanı maliyetiyle elde etmek için bu prototipi "miras alır".

JQuery'nin bunu nasıl doğru yaptığının hayati önem taşıyan bir kısmı, bunun programcıdan gizlenmiş olmasıdır. Kitaplığı kullanırken endişelenmeniz gereken bir şey olarak değil, tamamen bir optimizasyon olarak ele alınır.

JavaScript ile ilgili sorun, yalın yapıcı işlevlerin, arayan kişinin önek olarak bunları eklemeyi hatırlamasını gerektirmesidir, newaksi halde genellikle çalışmazlar. Bunun için iyi bir sebep yok. jQuery, bu saçmalığı sıradan bir işlevin arkasına saklayarak doğru yapar $, böylece nesnelerin nasıl uygulandığını umursamak zorunda kalmazsınız.

ECMAScript 5, belirli bir prototipe sahip bir nesneyi rahatça oluşturabilmeniz için standart bir işlev içerir Object.create. Oldukça basitleştirilmiş bir versiyonu şöyle görünecektir:

Object.create = function(prototype) {
    var Type = function () {};
    Type.prototype = prototype;
    return new Type();
};

Sadece bir yapıcı işlevi yazmanın ve sonra onu çağırmanın acısıyla ilgilenir new.

Prototiplerden ne zaman kaçınırsınız?

Java ve C # gibi popüler OO dilleriyle kullanışlı bir karşılaştırma yapılır. Bunlar iki tür kalıtımı destekler:

  • arayüz Eğer kalıtım, implementbir interfacetür sınıf arayüzünün her üyesi için kendi benzersiz uygulamasını sağladığından.
  • bazı yöntemlerin varsayılan uygulamalarını sağlayan extendbir uygulama mirası class.

JavaScript'te prototip kalıtım bir tür uygulama mirasıdır. Dolayısıyla, (C # veya Java'da) varsayılan davranışı elde etmek için temel bir sınıftan türetilmiş olduğunuz, daha sonra geçersiz kılmalar yoluyla küçük değişiklikler yaptığınız bu durumlarda, JavaScript'te prototip kalıtım anlamlıdır.

Bununla birlikte, C # veya Java'da arayüzler kullanmanız gereken bir durumdaysanız, JavaScript'te herhangi bir özel dil özelliğine ihtiyacınız yoktur. Arayüzü temsil eden bir şeyi açıkça bildirmeye gerek yoktur ve nesneleri bu arayüzü "uyguluyor" olarak işaretlemeye gerek yoktur:

var duck = {
    quack: function() { ... }
};

duck.quack(); // we're satisfied it's a duck!

Diğer bir deyişle, her bir nesne "türü" kendi "yöntem" tanımlarına sahipse, o zaman bir prototipten miras almanın bir değeri yoktur. Bundan sonra, her türden kaç örnek ayırdığınıza bağlıdır. Ancak birçok modüler tasarımda, belirli bir tipin yalnızca bir örneği vardır.

Ve aslında, birçok kişi tarafından uygulama mirasının kötü olduğu öne sürülmüştür . Yani, bir tür için bazı ortak işlemler varsa, o zaman bir temel / süper sınıfa konulmamaları, bunun yerine nesneyi (leri) ilettiğiniz bazı modüllerde sıradan işlevler olarak ortaya çıkmaları daha net olabilir. onların üzerinde işlem yapmalarını istiyorsun.


3
İyi açıklama. Öyleyse, prototipleri bir optimizasyon olarak gördüğünüze göre, her zaman kodunuzu geliştirmek için kullanılabileceklerine katılıyor musunuz? Prototip kullanmanın anlamsız olduğu veya gerçekten bir performans cezasına yol açtığı durumlar olup olmadığını merak ediyorum.
opl

Takibinizde, "bu, her türden kaç örnek ayırdığınıza bağlıdır" diyorsunuz. Ama bahsettiğiniz örnek prototipler kullanmıyor. Örnek tahsis etme nosyonu nerede (burada hala "yeni" kullanıyor olur musunuz)? Ayrıca: quack yönteminin bir parametresi olduğunu söyleyin - her duck.quack (param) çağrısı, bellekte yeni bir nesnenin oluşturulmasına neden olur mu (bir parametresi varsa veya olmasa da ilgisiz olabilir)?
opl

3
1. Bir tür ördek için çok sayıda örnek olması durumunda, örneği, quackişlev birçok ördek örneğinin bağlı olduğu bir prototipte olacak şekilde değiştirmenin mantıklı olacağını kastettim . 2. Nesne değişmez sözdizimi { ... }bir örnek oluşturur ( newonunla kullanmaya gerek yoktur ). 3. Herhangi bir JS işlevinin çağrılması, bellekte en az bir nesnenin oluşturulmasına neden olur - buna argumentsnesne denir ve çağrıda iletilen argümanları saklar: developer.mozilla.org/en/JavaScript/Reference/…
Daniel Earwicker

Teşekkürler Cevabınızı kabul ettim. Ama yine de düşüncenizle ilgili biraz kafa karışıklığım var (1): "Bir tür ördek için çok sayıda örnek" derken neyi kastettiğinizi anlamıyorum. (3) 'te söylediğin gibi, bir JS işlevini her çağırdığınızda, bellekte bir nesne oluşturulur - bu nedenle, yalnızca bir ördek türünüz olsa bile, ördek işlevini her çağırdığınızda bellek ayırmaz mıydınız? hangi durumda bir prototip kullanmak her zaman mantıklı olacaktır)?
opl

11
+1 jQuery ile yapılan karşılaştırma, okuduğum prototiplerin ne zaman ve neden kullanılacağına dair ilk açık ve öz açıklamaydı. Çok teşekkür ederim.
GFoley83

46

Nesnenin "statik olmayan" bir yöntemini bildirmek istiyorsanız, prototipler kullanmalısınız.

var myObject = function () {

};

myObject.prototype.getA = function (){
  alert("A");
};

myObject.getB = function (){
  alert("B");
};

myObject.getB();  // This works fine

myObject.getA();  // Error!

var myPrototypeCopy = new myObject();
myPrototypeCopy.getA();  // This works, too.

@keatsKelleher, ancak sadece yapıcı işlevi içindeki yöntemi thisörnek kullanarak tanımlayarak nesne için statik olmayan bir yöntem oluşturabiliriz this.getA = function(){alert("A")}değil mi?
Amr Labib

17

Yerleşik kullanmanın bir nedeni prototype nesneyi ortak işlevselliği paylaşacak bir nesneyi birden çok kez kopyalayacak olmanızdır. Prototipe yöntemler ekleyerek, her newörnek için oluşturulan çoğaltma yöntemlerinden tasarruf edebilirsiniz . Ancak öğesine bir yöntem eklediğinizde prototype, tüm örnekler bu yöntemlere erişebilir.

Temel bir Car()sınıfınız / nesneniz olduğunu varsayalım.

function Car() {
    // do some car stuff
}

sonra birden çok Car()örnek oluşturursunuz .

var volvo = new Car(),
    saab = new Car();

Artık her arabanın sürmesi, açılması vb. Gerektiğini biliyorsunuz. Car()Sınıfa doğrudan bir yöntem eklemek yerine (oluşturulan her örnek için belleği kaplar), bunun yerine yöntemleri prototipe ekleyebilirsiniz (yalnızca yöntemleri bir kez), bu nedenle hem yeni için bu yöntemlere erişim sağlayarak volvove saab.

// just mapping for less typing
Car.fn = Car.prototype;

Car.fn.drive = function () {
    console.log("they see me rollin'");
};
Car.fn.honk = function () {
    console.log("HONK!!!");
}

volvo.honk();
// => HONK!!!
saab.drive();
// => they see me rollin'

2
aslında bu yanlış. prototip nesnesini tamamen değiştirdiğiniz, genişletmediği için volvo.honk () çalışmayacaktır. Böyle bir şey yapacak olsaydınız, beklediğiniz gibi çalışır: Car.prototype.honk = function () {console.log ('HONK');} volvo.honk (); // 'HONK'
29er

1
@ 29er - bu örneği yazdığım şekilde, haklısınız. Sıra önemlidir. Bu örneği olduğu gibi tutacak olsaydım, bu jsfiddle: jsfiddle.net/mxacA'da gösterildiği gibi a'yı Car.prototype = { ... }çağırmadan önce new Car()gelmem gerekirdi . İddianıza gelince , bunu yapmanın doğru yolu bu olacaktır: jsfiddle.net/Embnp . Komik olan şu ki, bu soruyu cevapladığımı hatırlamıyorum =)
hellatan

@hellatan, prototip özelliğinin üzerine bir nesne değişmezi yazdığınız için yapıcı: Araba olarak ayarlayarak bunu düzeltebilirsiniz.
Josh Bedo

@josh bunu belirttiğiniz için teşekkürler. Cevabımı, başlangıçtan beri olması gerektiği gibi, prototipin üzerine değişmez bir nesne ile yazmamak için güncelledim.
hellatan

12

Belirli bir tür nesnenin çok sayıda kopyasını oluşturacağınız ve hepsinin ortak davranışları paylaşması gerektiğinde, işlevleri prototip nesneye koyun. Bunu yaparak, her işlevin yalnızca bir kopyasına sahip olarak bellekten tasarruf edersiniz, ancak bu yalnızca en basit faydadır.

Prototip nesnelerindeki yöntemleri değiştirmek veya yöntemler eklemek, karşılık gelen türlerin tüm örneklerinin doğasını anında değiştirir.

Şimdi tüm bunları tam olarak neden yaptığınız, çoğunlukla kendi uygulama tasarımınızın bir işlevidir ve istemci tarafı kodunda yapmanız gereken şeylerdir. (Tamamen farklı bir hikaye, bir sunucunun içindeki kod olabilir; orada daha büyük ölçekli "OO" kodu yapmayı hayal etmek çok daha kolay.)


bu yüzden prototip yöntemleriyle (yeni anahtar sözcük aracılığıyla) yeni bir nesneyi başlattığımda, bu nesne her işlevin yeni bir kopyasını almaz (sadece bir tür işaretçi)? Durum buysa, neden bir prototip kullanmak istemeyesiniz?
opl

@marcel, d'oh ... =) gibi
hellatan

@opi evet, haklısınız - kopya yapılmadı. Bunun yerine, prototip nesnesindeki semboller (özellik adları), her örnek nesnenin sanal parçaları olarak doğal olarak "oradadır". İnsanların bununla uğraşmak istememelerinin tek nedeni, nesnelerin kısa ömürlü ve farklı olduğu veya paylaşılacak çok fazla "davranış" olmadığı durumlar olabilir.
Pointy

3

Sınıf bazında açıklarsam Person sınıftır, walk () Prototip metodudur. Yani walk (), ancak bununla yeni nesneyi somutlaştırdıktan sonra varlığını sürdürecektir.

Dolayısıyla, Kişi gibi nesnelerin kopyalarını oluşturmak istiyorsanız, birçok kullanıcı oluşturabilirsiniz Prototip, bellekteki nesnelerin her biri için aynı işlev kopyasını paylaşarak / devralarak bellekten tasarruf sağladığı için iyi bir çözümdür.

Oysa statik böyle bir senaryoda o kadar da yardımcı olmaz.

function Person(){
this.name = "anonymous";
}

// its instance method and can access objects data data 
Person.prototype.walk = function(){
alert("person has started walking.");
}
// its like static method
Person.ProcessPerson = function(Person p){
alert("Persons name is = " + p.name);
}

var userOne = new Person();
var userTwo = new Person();

//Call instance methods
userOne.walk();

//Call static methods
Person.ProcessPerson(userTwo);

Yani bununla daha çok örnek yöntemi. Nesnenin yaklaşımı Statik yöntemler gibidir.

https://developer.mozilla.org/en/Introduction_to_Object-Oriented_JavaScript

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.