JavaScript .prototype nasıl çalışır?


2041

Ben dinamik programlama dillerinde değilim ama JavaScript kodunda adil payımı yazdım. Bu prototip tabanlı programlamayı hiç düşünmedim, kimse bunun nasıl çalıştığını biliyor mu?

var obj = new Object();
obj.prototype.test = function() { alert('Hello?'); };
var obj2 = new obj();
obj2.test();

Bir süre önce insanlarla yaptığım pek çok tartışmayı hatırlıyorum (ne yaptığımı tam olarak bilmiyorum) ama anladığım kadarıyla sınıf kavramı yok. Bu sadece bir nesne ve bu nesnelerin örnekleri orijinalin klonları, değil mi?

Ancak bu ".prototype" özelliğinin JavaScript'teki amacı nedir? Örnekleme nesneleriyle nasıl bir ilişkisi var?

Güncelleme: doğru yol

var obj = new Object(); // not a functional object
obj.prototype.test = function() { alert('Hello?'); }; // this is wrong!

function MyObject() {} // a first class functional object
MyObject.prototype.test = function() { alert('OK'); } // OK

Ayrıca bu slaytlar gerçekten çok yardımcı oldu.


78
John Resig, konuya bakarken bana yardımcı olan işlev prototipleri üzerinde birkaç slayt içeriyor (kodda değişiklik yapabilir ve ne olduğunu da görebilirsiniz ...) http://ejohn.org/apps/learn/#64
John Foster

5
Harika bir referans materyali, bu soruyu bilgilendirici tutmak amacıyla, sitenizin bağlantınızın artık kullanılamayacağı şekilde değişmesi durumunda, belki de John'un sitesinden gelen bazı yorumları cevabınıza yerleştirin. Her iki durumda da +1 bana yardımcı oldu.
Chris

95
John Resig'in 64 numaralı JavaScript Ninja slaytına bağlantı için +1 . Oradan başlamak gerçekten yardımcı oldu ve prototipleri doğru anladığımı hissediyorum.
ücretli bir inek

4
Prototip uygulamak için gerçekten işlevsel bir nesneye ihtiyacımız var mı? evet ise neden daha?
Anshul

6
Bu size yardımcı olabilir: webdeveasy.com/javascript-prototype
Naor

Yanıtlar:


1007

Her JavaScript nesnesi dahili bir "yuva" vardır denir [[Prototype]], değeri ya nullveya object. Bir yuvayı, yazdığınız koddan gizlenen JavaScript motorunun içinde bulunan bir nesne üzerindeki bir özellik olarak düşünebilirsiniz. Etrafındaki köşeli parantezler [[Prototype]]kasıtlıdır ve dahili yuvaları göstermek için ECMAScript spesifikasyon kuralına sahiptir.

[[Prototype]]Bir nesnenin işaret ettiği değer , konuşma dilinde "o nesnenin prototipi" olarak bilinir.

Bir özelliğe dot ( obj.propName) veya bracket ( obj['propName']) notasyonu ile erişirseniz ve nesnenin böyle bir özelliği doğrudan yoksa (yani bir , üzerinden kontrol edilebilen özellikobj.hasOwnProperty('propName') ), çalışma zamanı başvurulan nesnede bu ada sahip bir özellik arar tarafından [[Prototype]]yerine. Eğer [[Prototype]] aynı zamanda böyle bir özelliği yok, onun [[Prototype]]da kontrol ve benzeri edilir. Bu şekilde, orijinal nesnenin prototip zinciri bir eşleşme bulunana veya sonuna ulaşılana kadar yürür. Prototip zincirinin en üstünde nulldeğer bulunur.

Modern JavaScript uygulamaları [[Prototype]], aşağıdaki yollarla okuma ve / veya yazma erişimine izin verir :

  1. newOperatör (yapılandırır yapıcı işlevi döndürülen varsayılan nesne üzerinde prototip zinciri),
  2. extendsAnahtar, (sınıf sözdizimini kullanıldığında prototip zincirini yapılandırır)
  3. Object.createsağlanan bağımsız değişkeni [[Prototype]]ortaya çıkan nesnenin
  4. Object.getPrototypeOf ve Object.setPrototypeOf ( nesne oluşturulduktan [[Prototype]] sonra alın / ayarlayın ) ve
  5. Adlandırılmış standart erişimci (ör. Getter / setter) özelliği __proto__(4'e benzer)

Object.getPrototypeOfve kısmen Object.setPrototypeOftercih edildikleri __proto__içino.__proto__ bir nesnenin prototipi olduğunda olağandışı olduğundannull .

Bir nesnenin nesnesi [[Prototype]]başlangıçta nesne oluşturulurken ayarlanır.

Üzerinden yeni bir nesne oluşturursanız new Func() , nesnenin [[Prototype]]varsayılan değeri tarafından başvurulan nesneye ayarlanır Func.prototype.

Bu nedenle, tüm sınıfların ve işleçle kullanılabilen tüm işlevlerin new, kendilerine .prototypeek olarak adlandırılmış bir özelliğe sahip olduğunu unutmayın.[[Prototype]] iç yuvalarına . "Prototip" kelimesinin bu ikili kullanımı, yeni gelenler arasında sonsuz karışıklığın kaynağıdır.

kullanma newYapıcı işlevleriyle , JavaScript'te klasik kalıtımı simüle etmemizi sağlar; JavaScript'in miras sistemi - gördüğümüz gibi - prototipik ve sınıf tabanlı değil.

Sınıf sözdiziminin JavaScript'e girişinden önce, sınıfları simüle etmenin tek yolu yapıcı işlevleri idi. Yapıcı işlevi tarafından başvurulan nesnenin özelliklerini düşünebiliriz.prototype özelliği paylaşılan üyeler olarak ; yani. her örnek için aynı olan üyelerdir. Sınıf tabanlı sistemlerde, yöntemler her örnek için aynı şekilde uygulanır, bu nedenle yöntemler kavramsal olarak .prototypeözelliğe eklenir ; ancak bir nesnenin alanları örneğe özgüdür ve bu nedenle yapım sırasında nesnenin kendisine eklenir.

Sınıf sözdizimi olmadan, geliştiriciler klasik mirasa benzer işlevsellik elde etmek için prototip zincirini manuel olarak yapılandırmak zorunda kaldılar. Bu, bunu başarmanın farklı yollarının baskın olmasına yol açtı.

İşte bir yol:

function Child() {}
function Parent() {}
Parent.prototype.inheritedMethod = function () { return 'this is inherited' }

function inherit(child, parent) {
  child.prototype = Object.create(parent.prototype)
  child.prototype.constructor = child
  return child;
}

Child = inherit(Child, Parent)
const o = new Child
console.log(o.inheritedMethod()) // 'this is inherited'

... ve işte başka bir yol:

function Child() {}
function Parent() {}
Parent.prototype.inheritedMethod = function () { return 'this is inherited' }

function inherit(child, parent) {
    function tmp() {}
    tmp.prototype = parent.prototype
    const proto = new tmp()
    proto.constructor = child
    child.prototype = proto
    return child
}

Child = inherit(Child, Parent)
const o = new Child
console.log(o.inheritedMethod()) // 'this is inherited'

ES2015'te sunulan sınıf sözdizimi, aşağıdakileri sağlayarak işleri kolaylaştırır: extends , JavaScript'te klasik kalıtımı simüle etmek için prototip zincirini yapılandırmak için "tek doğru yol" .

Yani, yukarıdaki koda benzer şekilde, sınıf sözdizimini aşağıdaki gibi yeni bir nesne oluşturmak için kullanırsanız:

class Parent { inheritedMethod() { return 'this is inherited' } }
class Child extends Parent {}

const o = new Child
console.log(o.inheritedMethod()) // 'this is inherited'

... çıkan nesne en [[Prototype]]örneğine ayarlanacak Parentolan, [[Prototype]]sırayla, birParent.prototype .

Son olarak, üzerinden yeni bir nesne oluşturursanız Object.create(foo), elde edilen nesnenin [[Prototype]]değeri olarak ayarlanır foo.


1
Yani, kısa snippet'imdeki prototip özelliği üzerinde yeni özellikler tanımlayarak yanlış bir şey yapıyorum?
John Leidegren

3
Birinci sınıf vatandaşlar olarak fonksiyon nesnelerine sahip olmanın anlamı budur.
John Leidegren

8
Özellikle programlama dillerinde standart olmayan şeylerden nefret ediyorum, açıkça gerekmediğinde neden bir protokol bile var?
John Leidegren

1
@John __proto__ yalnızca JS yorumlayıcısı tarafından dahili kullanım için gereklidir. Her Nesnenin hangi özellik ve yöntemlerin prototipin ve hangisinin Nesnenin kendisinin bir parçası olduğunu bilmesi gerekir. JS yorumlayıcısının bir Object üzerindeki prototip yöntemlerini çağırabilmesi gerekir.
BMiner

17
kullanımı bu not [[Prototip]] kasıtlı - çift köşeli parantez iç özellikleri, ECMA-262 bir vaziyette isimleri
Christoph

1798

Java, C # veya C ++ gibi klasik kalıtım uygulayan bir dilde, bir sınıf - nesneleriniz için bir plan - oluşturarak başlıyorsunuz ve daha sonra bu sınıftan yeni nesneler oluşturabilir veya sınıfı genişleterek, artırılan yeni bir sınıfı tanımlayabilirsiniz. orijinal sınıf.

JavaScript'te önce bir nesne yaratırsınız (sınıf kavramı yoktur), ardından kendi nesnenizi artırabilir veya ondan yeni nesneler oluşturabilirsiniz. Zor değil, ama klasik şekilde alışmış biri için biraz yabancı ve metabolize etmek zor.

Misal:

//Define a functional object to hold persons in JavaScript
var Person = function(name) {
  this.name = name;
};

//Add dynamically to the already defined object a new getter
Person.prototype.getName = function() {
  return this.name;
};

//Create a new object of type Person
var john = new Person("John");

//Try the getter
alert(john.getName());

//If now I modify person, also John gets the updates
Person.prototype.sayMyName = function() {
  alert('Hello, my name is ' + this.getName());
};

//Call the new method on john
john.sayMyName();

Şimdiye kadar temel nesneyi genişletiyordum, şimdi başka bir nesne oluşturuyorum ve sonra Kişi'den miras alıyorum.

//Create a new object of type Customer by defining its constructor. It's not 
//related to Person for now.
var Customer = function(name) {
    this.name = name;
};

//Now I link the objects and to do so, we link the prototype of Customer to 
//a new instance of Person. The prototype is the base that will be used to 
//construct all new instances and also, will modify dynamically all already 
//constructed objects because in JavaScript objects retain a pointer to the 
//prototype
Customer.prototype = new Person();     

//Now I can call the methods of Person on the Customer, let's try, first 
//I need to create a Customer.
var myCustomer = new Customer('Dream Inc.');
myCustomer.sayMyName();

//If I add new methods to Person, they will be added to Customer, but if I
//add new methods to Customer they won't be added to Person. Example:
Customer.prototype.setAmountDue = function(amountDue) {
    this.amountDue = amountDue;
};
Customer.prototype.getAmountDue = function() {
    return this.amountDue;
};

//Let's try:       
myCustomer.setAmountDue(2000);
alert(myCustomer.getAmountDue());

Dediğim gibi setAmountDue () çağıramazken, bir Kişiye getAmountDue ().

//The following statement generates an error.
john.setAmountDue(1000);

352
Stackoverflow ile ilgili cevaplar sadece orijinal poster için değil, aynı zamanda gizlenen veya aramalardan gelen diğer insanların büyük bir topluluğu için de ilginç olduğunu düşünüyorum. Ben de onlardan biriydim ve eski görevlerden faydalanıyordum. Bazı kod örnekleri ekleyerek diğer cevaplara katkıda bulunabilir düşünüyorum. Sorunuz hakkında: yeni olanı dışarıda bırakırsanız, işe yaramaz. myCustomer.sayMyName () öğesini çağırdığımda "myCustomer.sayMyName bir işlev değil" döndürür. En kolay yol kundakçı ile deney yapmak ve ne olduğunu görmektir.
stivlo

7
Anladığım kadarıyla var Person = function (name) {...}; Kişi Nesneleri oluşturabilecek bir yapıcı işlevi tanımlamaktadır. Bu yüzden henüz Nesne yok, yalnızca anonim yapıcı işlevi Kişiye atanır. Bu çok iyi bir açıklama: helephant.com/2008/08/how-javascript-objects-work
stivlo

17
UYARI: Bu yanıt, üst sınıf kurucusunun örnek başına çağrılmamasını ihmal eder. Çalışmasının tek nedeni, hem çocuk hem de ana kurucuda tam olarak aynı şeyi (adı ayarlayarak) yapmasıdır. JavaScript'te kalıtım girişimi (ve son bir çözüm) sırasında yapılan yaygın hatalar hakkında daha ayrıntılı bir açıklama için lütfen bakınız: bu yığın taşma direği
Aaren Cordova

3
Bu yanıtın prototip olarak "yeni Kişi ()" kullanarak, "Kişi" nin "name" örnek özelliğini "Müşteri" nin statik bir özelliği olarak ayarladığınızdan da bahsetmediğini fark ettim. örnekler aynı özelliğe sahip olacaktır). Bu iyi bir temel örnek olsa da, YAPMAYIN. :) Prototipini "Person.prototype" olarak ayarlayarak "köprü" olarak işlev görecek yeni bir anonim işlev oluşturun, ardından bir örnek oluşturun ve bunun yerine "Customer.prototype" ifadesini bu anonim örneğe ayarlayın.
James Wilkins

10
Hakkında Customer.prototype = new Person();hattı, MDN gösterileri örnek kullanılarak Customer.prototype = Object.create(Person.prototype)ve devletler bu 'Burada ortak bir hata 'Yeni Kişi ()' kullanmaktır' . kaynak
Rafael Eyng

186

Bu, açıklama sırasında henüz bir yorum yapılmadan örnek olarak kabul edilecek çok basit bir prototip tabanlı nesne modelidir:

function Person(name){
    this.name = name;
}
Person.prototype.getName = function(){
    console.log(this.name);
}
var person = new Person("George");

Prototip konseptine geçmeden önce dikkate almamız gereken bazı önemli noktalar vardır.

1- JavaScript işlevlerinin gerçekte nasıl çalıştığı:

İlk adımı atmak için, JavaScript işlevlerinin gerçekte nasıl çalıştığını, thisiçindeki anahtar kelimeyi kullanan bir sınıf gibi işlev veya bağımsız değişkenleriyle normal bir işlev olarak, ne yaptığını ve ne döndürdüğünü bulmamız gerekir.

Diyelim ki bir Personnesne modeli oluşturmak istiyoruz . ancak bu adımda aynı şeyi prototypeve newanahtar kelimeyi kullanmadan yapmaya çalışacağım .

Yani bu aşamada functions, objectsve thisanahtar kelime, tek sahip bulunmaktadır.

İlk soru, anahtar kelime kullanmadan anahtar kelimenin nasıl thisyararlı olabileceğidirnew .

Buna cevap vermek için boş bir nesnemiz olduğunu ve şöyle iki fonksiyonumuz olduğunu varsayalım:

var person = {};
function Person(name){  this.name = name;  }

function getName(){
    console.log(this.name);
}

ve şimdi kullanmadan newanahtar kelime biz bu işlevleri kullanabilirsiniz nasıl. Dolayısıyla JavaScript'in bunu yapmanın 3 farklı yolu vardır:

a. ilk yol, işlevi normal bir işlev olarak çağırmaktır:

Person("George");
getName();//would print the "George" in the console

bu durumda, bu genellikle windowtarayıcıdaki veya GLOBALiçindeki global nesne olan geçerli bağlam nesnesi olacaktır Node.js. Bu demektir ki, tarayıcıda window.name veya Node.js'de GLOBAL.name, değeri "George" olacaktı.

b. Biz yapabilirsiniz takmak onun özellikleri olarak, bir nesneye onları

- Bunu yapmanın en kolay yolu boş personnesneyi değiştirmek , örneğin:

person.Person = Person;
person.getName = getName;

bu şekilde onlara şöyle diyebiliriz:

person.Person("George");
person.getName();// -->"George"

ve şimdi personnesne şöyle:

Object {Person: function, getName: function, name: "George"}

- Bir nesneye bir özellik eklemek için diğer bir yolprototype , adı ile herhangi bir JavaScript nesnesinde bulunabilen bu nesnenin kullanarak __proto__, ve ben özet bölümünde biraz açıklamaya çalıştım. Böylece benzer sonucu elde edebiliriz:

person.__proto__.Person = Person;
person.__proto__.getName = getName;

Ancak bu şekilde yaptığımız şey şu şekilde değişiklik yapmaktır Object.prototype, çünkü değişmez değerler ( { ... }) kullanarak bir JavaScript nesnesi oluşturduğumuzda, buna dayalı olarak oluşturulur Object.prototype, yani yeni oluşturulan nesneye adlı bir öznitelik olarak eklenir __proto__, yani , önceki kod snippet'imizde yaptığımız gibi, tüm JavaScript nesneleri değişecekti, iyi bir uygulama değil. Peki şimdi daha iyi uygulama ne olabilir:

person.__proto__ = {
    Person: Person,
    getName: getName
};

ve şimdi diğer nesneler barış içinde, ama yine de iyi bir uygulama gibi görünmüyor. Bu yüzden hala bir çözümümüz daha var, ancak bu çözümü kullanmak için personnesnenin oluşturulduğu kod satırına geri dönmeliyiz ( var person = {};) ve sonra şu şekilde değiştirmeliyiz:

var propertiesObject = {
    Person: Person,
    getName: getName
};
var person = Object.create(propertiesObject);

yaptığı şey yeni bir JavaScript oluşturmak ve özelliğe Objecteklemek . Böylece yapabileceğinizden emin olmak için:propertiesObject__proto__

console.log(person.__proto__===propertiesObject); //true

Ancak buradaki zor nokta __proto__, personnesnenin ilk düzeyinde tanımlanan tüm özelliklere erişebilmenizdir (daha fazla ayrıntı için özet bölümünü okuyun).


Gördüğünüz gibi, bu iki yoldan herhangi biri kullanıldığında this, personnesneye tam olarak işaret edilir .

c. JavaScript ile işlevini sağlamak için başka bir yol vardır thiskullanıyor, çağrıyı veya uygulamak işlevini çağırmak için.

Apply () yöntemi, belirli bir değere sahip bir işlevi çağırır ve dizi (veya dizi benzeri bir nesne) olarak sağlanan bağımsız değişkenler.

ve

Call () yöntemi, verilen bu değere sahip bir işlevi ve bağımsız olarak sağlanan bağımsız değişkenleri çağırır.

bu şekilde benim favorim, fonksiyonlarımızı kolayca çağırabiliriz:

Person.call(person, "George");

veya

//apply is more useful when params count is not fixed
Person.apply(person, ["George"]);

getName.call(person);   
getName.apply(person);

bu 3 yöntem .prototype işlevselliğini anlamak için önemli ilk adımlardır.


2- newAnahtar kelime nasıl çalışır?

Bu .prototypeişlevselliği anlamak için ikinci adımdır. bu süreci simüle etmek için kullandığım şey:

function Person(name){  this.name = name;  }
my_person_prototype = { getName: function(){ console.log(this.name); } };

bu bölümünde ben de kullanmadan JavaScript alır tüm adımları atmaya çalışıyor olduğum newkelime ve prototypesen kullandığınızda, newanahtar kelime. Bu nedenle, yaptığımız zaman new Person("George"), Personişlev bir kurucu olarak işlev görür, Bunlar JavaScript'in yaptığı, birer birer:

a. her şeyden önce boş bir nesne, temelde boş bir karma yapar:

var newObject = {};

b. JavaScript'in atması gereken bir sonraki adım , tüm prototip nesnelerini yeni oluşturulan nesneye eklemektir

Elimizdeki my_person_prototypeburada prototip nesnesine benzer.

for(var key in my_person_prototype){
    newObject[key] = my_person_prototype[key];
}

JavaScript'in aslında prototipte tanımlanan özellikleri eklemesi böyle değildir. Gerçek yol, prototip zinciri konseptiyle ilgilidir.


a. & b. Bu iki adım yerine, tam olarak aynı sonucu elde edebilirsiniz:

var newObject = Object.create(my_person_prototype);
//here you can check out the __proto__ attribute
console.log(newObject.__proto__ === my_person_prototype); //true
//and also check if you have access to your desired properties
console.log(typeof newObject.getName);//"function"

şimdi getNamefonksiyonu çağırabiliriz bizim my_person_prototype:

newObject.getName();

c. sonra bu nesneyi kurucuya verir,

biz bunu bizim örnek ile yapabilirsiniz:

Person.call(newObject, "George");

veya

Person.apply(newObject, ["George"]);

Daha sonra yapıcı, çünkü istediği her şeyi yapabilir , bu o yapıcı içinde yeni oluşturulan nesnedir.

şimdi diğer adımları simüle etmeden önceki sonuç: Object {name: "George"}


Özet:

Temel olarak, bir işlevde yeni anahtar sözcüğü kullandığınızda , bunu çağırırsınız ve bu işlev bir yapıcı işlevi görür, bu nedenle şunları söylediğinizde:

new FunctionName()

JavaScript dahili olarak bir nesne, boş bir karma yapar ve daha sonra bu nesneyi kurucuya verir, sonra kurucu istediği her şeyi yapabilir, çünkü bu kurucunun içindeki yeni oluşturulan nesne ve daha sonra size elbette o nesneyi verir return deyimini işlevinizde kullanmadıysanız veya return undefined;işlev gövdesinin sonuna bir koyduysanız .

Dolayısıyla JavaScript bir nesne üzerindeki bir özelliği aramaya gittiğinde, yaptığı ilk şey, o nesne üzerinde görünmesidir. Ve [[prototype]]genellikle sahip olduğumuz gizli bir özellik var __proto__ve bu özellik JavaScript'in bir sonraki bakışta olduğunu gösteriyor. Ve tekrar baktığında __proto__, yine başka bir JavaScript nesnesi olduğu zaman, kendi __proto__niteliği vardır, bir sonraki __proto__boş olana ulaşıncaya kadar yukarı ve yukarı gider . Nokta, JavaScript'te __proto__özelliğinin null olduğu tek Object.prototypenesnedir:

console.log(Object.prototype.__proto__===null);//true

JavaScript'te kalıtım bu şekilde çalışır.

Prototip zinciri

Başka bir deyişle, bir işlev üzerinde bir prototip özelliğiniz olduğunda ve bunun üzerinde yeni bir çağrı yaptığınızda, JavaScript yeni özellikler için özellikler için bakmayı bitirdikten sonra, işlevin işlevlerine bakacaktır .prototypeve ayrıca bu nesnenin kendi iç prototipi. ve bunun gibi.


6
a) Lütfen prototipleri özellikleri kopyalayarak açıklamayın b) Dahili [[prototip]] 'in ayarlanması , örnek üzerinde yapıcı işlevi uygulanmadan önce gerçekleşir, lütfen bu sırayı değiştirin c) jQuery bu soruda tamamen çevrimdışıdır
Bergi

1
@Bergi: işaret ettiğin için teşekkürler, şimdi sorun olup olmadığını bana bildirirsen sevinirim.
Mehran Hatami

7
Lütfen basitleştirebilir misiniz? Her konuda haklısınız, ancak bu açıklamayı okuyan öğrenciler ilk kez gerçekten kafa karıştırıcı olabilir. daha basit bir örnek alın ve kodun kendisini açıklamasına veya ne demek istediğinizi açıklığa kavuşturmak için bir grup yorum eklemesine izin verin.
PM

2
@PM: Geri bildiriminiz için teşekkür ederiz. Mümkün olduğunca basit hale getirmeye çalıştım ama haklı olduğunu düşünüyorum, hala bazı belirsiz noktaları var. Bu yüzden onu değiştirmeye çalışacağım ve daha açıklayıcı olacağım. :)
Mehran Hatami

1
@AndreaMattioli çünkü bu şekilde tamamen yeni bir nesne yaratıyorsunuz ve diğer nesneler arasında da paylaşılabilen eski nesneyi geçersiz kılıyorsunuz. Değiştirerek __proto__önce tüm üst düzey prototip özelliklerini sileceksiniz ve daha sonra paylaşmadığınız sürece artık paylaşılmayan yeni bir proto tabanınız olacak.
Mehran Hatami

77

Yedi prototip

Ciro San, derin meditasyondan sonra Mount Fire Fox'a inerken, zihni açık ve huzurluydu.

Ancak eli huzursuzdu ve kendi başına bir fırça aldı ve aşağıdaki notları not etti.


0) İki farklı şey "prototip" olarak adlandırılabilir:

  • prototip özelliği, obj.prototype

  • [[Prototype]] ES5'teki gibi gösterilen prototip dahili özelliği .

    ES5 üzerinden alınabilir Object.getPrototypeOf().

    Firefox __proto__özelliği aracılığıyla bir uzantı olarak erişilebilir olmasını sağlar . ES6 şimdi için bazı isteğe bağlı gereksinimlerden bahsediyor__proto__ .


1) Bu kavramlar soruyu cevaplamak için vardır:

Yaptığımda obj.property, JS nerede arar .property?

Sezgisel olarak, klasik kalıtım mülk arayışını etkilemelidir.


2)

  • __proto__olduğu .gibi dot özellik araması için kullanılır obj.property.
  • .prototypeolduğu değil , doğrudan, dolaylı olarak da belirler olarak arama için kullanılan __proto__nesne oluşturma sırasında new.

Arama sırası:

  • objobj.p = ...veya ile eklenen özelliklerObject.defineProperty(obj, ...)
  • özellikleri obj.__proto__
  • özellikleri obj.__proto__.__proto__vb.
  • bazıları __proto__ise nullgeri dönün undefined.

Bu, sözde prototip zinciridir .

Ve .ile aramayı önleyebilirsinizobj.hasOwnProperty('key')Object.getOwnPropertyNames(f)


3) Ayarlamanın iki ana yolu vardır obj.__proto__:

  • new:

    var F = function() {}
    var f = new F()

    sonra newayarladı:

    f.__proto__ === F.prototype

    İşte burada .prototypekullanılır.

  • Object.create:

     f = Object.create(proto)

    kümeleri:

    f.__proto__ === proto

4) Kod:

var F = function(i) { this.i = i }
var f = new F(1)

Aşağıdaki şemaya karşılık gelir (bazı Numberşeyler atlanır):

(Function)       (  F  )                                      (f)----->(1)
 |  ^             | | ^                                        |   i    |
 |  |             | | |                                        |        |
 |  |             | | +-------------------------+              |        |
 |  |constructor  | |                           |              |        |
 |  |             | +--------------+            |              |        |
 |  |             |                |            |              |        |
 |  |             |                |            |              |        |
 |[[Prototype]]   |[[Prototype]]   |prototype   |constructor   |[[Prototype]]
 |  |             |                |            |              |        |
 |  |             |                |            |              |        |
 |  |             |                | +----------+              |        |
 |  |             |                | |                         |        |
 |  |             |                | | +-----------------------+        |
 |  |             |                | | |                                |
 v  |             v                v | v                                |
(Function.prototype)              (F.prototype)                         |
 |                                 |                                    |
 |                                 |                                    |
 |[[Prototype]]                    |[[Prototype]]          [[Prototype]]|
 |                                 |                                    |
 |                                 |                                    |
 | +-------------------------------+                                    |
 | |                                                                    |
 v v                                                                    v
(Object.prototype)                                       (Number.prototype)
 | | ^
 | | |
 | | +---------------------------+
 | |                             |
 | +--------------+              |
 |                |              |
 |                |              |
 |[[Prototype]]   |constructor   |prototype
 |                |              |
 |                |              |
 |                | -------------+
 |                | |
 v                v |
(null)           (Object)

Bu şemada önceden tanımlanmış birçok dil nesne düğümü gösterilmektedir:

  • null
  • Object
  • Object.prototype
  • Function
  • Function.prototype
  • 1
  • Number.prototype( (1).__proto__sözdizimini karşılamak için parantez zorunludur)

2 kod satırımız yalnızca aşağıdaki yeni nesneleri oluşturdu:

  • f
  • F
  • F.prototype

işu anda bir özelliktir, fçünkü bunu yaptığınızda:

var f = new F(1)

Bu değerlendirir File thisdeğerini newdaha sonra atanır dönecektir f.


5) .constructor normalde arama F.prototypeyoluyla gelir .:

f.constructor === F
!f.hasOwnProperty('constructor')
Object.getPrototypeOf(f) === F.prototype
F.prototype.hasOwnProperty('constructor')
F.prototype.constructor === f.constructor

Yazdığımızda f.constructor, JavaScript .aramayı şu şekilde yapar:

  • f yok .constructor
  • f.__proto__ === F.prototypevar .constructor === F, öyleyse al

Sonuç f.constructor == Fsezgisel olarak doğrudur, çünkü klasik OOP dillerinde olduğu gibi set alanları Foluşturmak için kullanılır f.


6) Klasik kalıtım sözdizimi prototip zincirleri manipüle edilerek sağlanabilir.

ES6 , daha önce olası prototip manipülasyon deliliği için çoğunlukla sözdizimi şekeri olan classve extendsanahtar kelimelerini ekler .

class C {
    constructor(i) {
        this.i = i
    }
    inc() {
        return this.i + 1
    }
}

class D extends C {
    constructor(i) {
        super(i)
    }
    inc2() {
        return this.i + 2
    }
}
// Inheritance syntax works as expected.
c = new C(1)
c.inc() === 2
(new D(1)).inc() === 2
(new D(1)).inc2() === 3
// "Classes" are just function objects.
C.constructor === Function
C.__proto__ === Function.prototype
D.constructor === Function
// D is a function "indirectly" through the chain.
D.__proto__ === C
D.__proto__.__proto__ === Function.prototype
// "extends" sets up the prototype chain so that base class
// lookups will work as expected
var d = new D(1)
d.__proto__ === D.prototype
D.prototype.__proto__ === C.prototype
// This is what `d.inc` actually does.
d.__proto__.__proto__.inc === C.prototype.inc
// Class variables
// No ES6 syntax sugar apparently:
// http://stackoverflow.com/questions/22528967/es6-class-variable-alternatives
C.c = 1
C.c === 1
// Because `D.__proto__ === C`.
D.c === 1
// Nothing makes this work.
d.c === undefined

Önceden tanımlanmış tüm nesneler olmadan basitleştirilmiş diyagram:

(c)----->(1)
 |   i
 |
 |
 |[[Prototype]]
 |
 |
 v    __proto__
(C)<--------------(D)         (d)
| |                |           |
| |                |           |
| |prototype       |prototype  |[[Prototype]] 
| |                |           |
| |                |           |
| |                | +---------+
| |                | |
| |                | |
| |                v v
|[[Prototype]]    (D.prototype)--------> (inc2 function object)
| |                |             inc2
| |                |
| |                |[[Prototype]]
| |                |
| |                |
| | +--------------+
| | |
| | |
| v v
| (C.prototype)------->(inc function object)
|                inc
v
Function.prototype

Aşağıdakilerin nasıl çalıştığını inceleyelim:

c = new C(1)
c.inc() === 2

İlk satır kümeleri c.iiçin 1olduğu gibi ") 4" olarak açıklanmıştır.

İkinci satırda:

c.inc()
  • .incaracılığıyla bulunursa [[Prototype]]zinciri: c-> C-> C.prototype->inc
  • Javascript'te bir işlevi şu şekilde çağırdığımızda X.Y(), JavaScript otomatik olarak işlev çağrısının içinde thiseşit olarak ayarlanır !XY()

Aynı mantık d.incve açıklar d.inc2.

Bu makalede https://javascript.info/class#not-just-a-syntax-sugarclass bilmeye değer diğer etkilerden bahsedilmektedir . Bazılarına classanahtar kelime olmadan ulaşılamayabilir (hangisini kontrol ederseniz):


1
@tomasb teşekkürler! "Bunu nereden bulduğunuzu bilmiyorum": bu dinamik dillerden birkaçını gördükten sonra, sınıf sistemleriyle ilgili en önemli şeyin, .aramanın nasıl çalıştığı (ve verilerin kaç kopyasının yapıldığı) olduğunu fark ettim. . Ben de bu noktayı anlamaya başladım. Geri kalanı Google + blog yayınları + eldeki Js tercümanı. :)
Ciro Santilli 法轮功 病毒 审查 六四 事件 法轮功

1
Hala neden g.constructor === nesne anlamıyorum çünkü "4) f = yeni F yaptığınızda, yeni de f.constructor = F" ayarlar. Bana daha fazla açıklayabilir misin? Her neyse, aradığım en iyi cevap bu. Çok teşekkür ederim!
nguyenngoc101

@ nguyenngoc101 teşekkürler! sets f.constructor = FBölüm pervasızca yanlış ve daha ileri bölümleri ile çelişmektedir oldu: .constructoraracılığıyla bulunursa .prototip zincirinde arama. Şimdi düzeltildi.
Ciro Santilli 法轮功 11: 审查 六四 事件 法轮功

Yapıcı işlevi oluşturup yeni bir işleç kullanarak bunun örneğini oluşturmaya çalışırsam, aldığım tüm tartışmalardan (klasik miras geldi), yalnızca proto nesnesine eklenen yöntemleri ve özellikleri alacağım, bu nedenle tüm yöntemi eklemek gerekir ve mi devralmak istiyorsak proto nesnesinin özelliklerini mi?
blackHawk

1
@CiroSantilli Ch 死 六四 事件 法轮功 Bunun Chromium'da bir hata olduğunu düşünmüyorum. Bence fprototip Fsadece yapım aşamasında ortaya çıkan bir belirti ; ilk kurulduktan sonra hiçbir zaman fbilemez veya ilgilenmez F.prototype.
John Glassmyer

76

prototypesınıf yapmanıza izin verir. Eğer kullanmazsanız prototypestatik olur.

İşte kısa bir örnek.

var obj = new Object();
obj.test = function() { alert('Hello?'); };

Yukarıdaki durumda, statik işlev çağırma testine sahipsiniz. Bu işleve yalnızca, obj'in bir sınıf olduğunu hayal edebileceğiniz obj.test tarafından erişilebilir.

Aşağıdaki kodda olduğu gibi

function obj()
{
}

obj.prototype.test = function() { alert('Hello?'); };
var obj2 = new obj();
obj2.test();

Objektif şimdi somutlaştırılabilecek bir sınıf haline geldi. Birden fazla obj örneği mevcut olabilir ve hepsinin testişlevi vardır.

Yukarıdakiler benim anlayışım. Bunu bir topluluk wiki'si yapıyorum, böylece yanılıyorsam insanlar beni düzeltebilir.


13
-1: prototypeörneklerin değil, yapıcı işlevlerinin bir özelliğidir, yani kodunuz yanlış! Belki de __proto__nesnelerin standart dışı özelliğini kastediyordunuz , ama bu tamamen farklı bir canavar ...
Christoph

@Christoph - Gösterdiğiniz için teşekkürler. Örnek kodu güncelledim.
Ramesh

3
Çok daha fazlası var ... Ayrıca JavaScript sınıf tabanlı bir dil değil - prototipler aracılığıyla kalıtımla ilgileniyor, farklılıkları daha ayrıntılı olarak ele almanız gerekiyor!
James

5
Bence bu cevap biraz yanlış yönlendirici.
Armin Cifuentes

Belki cevap 'yanlış yönlendirmektir', ancak prototipin ne için kullanıldığını açıklıyor ve şimdi yüzlerce oyla bu 'cevaplardan' sonra hepsi benim için açık. Teşekkür ederim.
Aleks

66

Bu konuyu okuduktan sonra JavaScript Prototip Zinciri ile kafam karıştı, sonra bu grafikleri buldum

http://iwiki.readthedocs.org/en/latest/javascript/js_core.html#inheritance * [[prototip]] * ve fonksiyon nesnelerinin <code> prototip </code> özelliği

Prototip Zinciri ile JavaScript Devralmasını göstermek için net bir grafik

ve

http://www.javascriptbank.com/javascript/article/JavaScript_Classical_Inheritance/

bu kod ve birkaç güzel diyagram içeren bir örnek içerir.

prototip zinciri nihayetinde Object.prototype'e geri döner.

prototip zinciri, alt sınıfın prototipini üst sınıftaki bir nesneye eşit olarak ayarlayarak istediğiniz kadar teknik olarak genişletilebilir.

Umarım JavaScript Prototip Zincirini anlamanız da yararlı olur.


Javascript'te birden fazla miras olması mümkün müdür?

Foo burada bir nesne mi yoksa işlev nesnesi mi? Nesne değişmezse, Foo.prototype'in kurucu aracılığıyla Foo'ya dönmeyeceğine inanıyorum.
Madhur Ahuja

@ user3376708 JavaScript yalnızca tek mirası ( kaynak ) destekler
Rafael Eyng

@ Nuno_147 İlk başta net değil, ama yeterince uzun bakarsanız bir şey çıkarabilirsiniz.
marcelocra

3
Ne [[Prototype]]anlama geldiğini açıklayabilir misiniz ?
CodyBugstein

40

Her nesnenin, [[Prototip]] adlı bir dahili özelliği vardır, onu başka bir nesneye bağlar:

object [[Prototype]]  anotherObject

Geleneksel javascriptte, bağlantılı nesne prototypebir fonksiyonun özelliğidir:

object [[Prototype]]  aFunction.prototype

Bazı ortamlar [[Prototip]] 'i şu şekilde ortaya koyar__proto__ :

anObject.__proto__ === anotherObject

Oluşturabilir [[Prototip]] bir nesne oluşturarak linki.

// (1) Object.create:
var object = Object.create(anotherObject)
// object.__proto__ = anotherObject

// (2) ES6 object initializer:
var object = { __proto__: anotherObject };
// object.__proto__ = anotherObject

// (3) Traditional JavaScript:
var object = new aFunction;
// object.__proto__ = aFunction.prototype

Yani bu ifadeler eşdeğerdir:

var object = Object.create(Object.prototype);
var object = { __proto__: Object.prototype }; // ES6 only
var object = new Object;

Bağlantı hedefini ( Object.prototype) yeni bir ifadede göremezsiniz ; bunun yerine hedef yapıcı ( Object) tarafından ima edilir .

Hatırlamak:

  • Her nesnenin bir bağlantısı vardır [[Prototip]] , bazen __proto__ olarak görünür .
  • Her işlevin prototypebaşlangıçta boş bir nesne tutan bir özelliği vardır .
  • İle oluşturulan nesneler yeni bağlantılıdır prototypeonların yapıcı özelliğine.
  • Bir işlev hiçbir zaman kurucu olarak kullanılmazsa prototypeözelliği kullanılmaz.
  • Bir kurucuya ihtiyacınız yoksa, Object.create yerine kullanın new.

1
Revizyon 5, Object.create () ile ilgili bilgiler de dahil olmak üzere bazı yararlı bilgileri kaldırdı. Bkz. Revizyon 4 .
Palec

@Palec ne eklemeliyim?
sam

2
IMO en azından Object.create()dokümanlara bağlantı , @sam. Linkler __proto__ve Object.prototypegüzel geliştirmeler olurdu. Prototiplerin kurucularla nasıl çalıştığına dair örneklerinizi beğendim ve Object.create()muhtemelen kurtulmak istediğiniz uzun ve daha az alakalı kısımdı.
Palec

Yapıcı işlevi oluşturup yeni bir işleç kullanarak bunun örneğini oluşturmaya çalışırsam, aldığım tüm tartışmalardan (klasik miras geldi), yalnızca proto nesnesine eklenen yöntemleri ve özellikleri alacağım, bu nedenle tüm yöntemi eklemek gerekir ve mi devralmak istiyorsak proto nesnesinin özelliklerini mi?
blackHawk

29

Javascript normal anlamda mirasa sahip değildir, ancak prototip zincirine sahiptir.

prototip zinciri

Bir nesnenin bir üyesi nesnede bulunamazsa, onu prototip zincirinde arar. Zincir diğer nesnelerden oluşur. Belirli bir örneğin prototipine __proto__değişkenle erişilebilir . Javascript'teki sınıflar ve örnekler arasında fark olmadığı için her nesnenin bir nesnesi vardır.

Prototipe bir işlev / değişken eklemenin avantajı, her örnek için değil, yalnızca bir kez bellekte bulunması gerektiğidir.

Prototip zinciri başka birçok nesneden de oluşabileceğinden, kalıtım için de yararlıdır.


1
FF ve Chrome protokolü destekler , ancak IE veya Opera'yı desteklemez.
Bazı

Georg, lütfen bir noob için açıklığa kavuşun - "Javascript'teki sınıflar ve örnekler arasında hiçbir fark yoktur." - detaylandırabilir misin? Bu nasıl çalışıyor?
Hamish Grubijan

Yapıcı işlevi oluşturup yeni bir işleç kullanarak bunun örneğini oluşturmaya çalışırsam, aldığım tüm tartışmalardan (klasik miras geldi), yalnızca proto nesnesine eklenen yöntemleri ve özellikleri alacağım, bu nedenle tüm yöntemi eklemek gerekir ve mi devralmak istiyorsak proto nesnesinin özelliklerini mi?
blackHawk

28

Bu makale uzundur. Ancak eminim ki JavaScript Kalıtım'ın "prototip" doğasıyla ilgili sorgularınızın çoğunu temizleyecektir. Ve daha da fazlası. Lütfen makalenin tamamını okuyun.

JavaScript temel olarak iki tür veri türüne sahiptir

  • Nesne olmayan
  • Nesneler

Nesne olmayan

Aşağıda, Sigara nesne veri tipleri

  • sicim
  • sayı (NaN ve Infinity dahil)
  • boole değerleri (doğru, yanlış)
  • Tanımsız

Bu veri türleri, typeof komutunu kullandığınızda operatörünü

typeof "string literal" (veya string literal içeren bir değişken) === 'string'

typeof 5 (veya herhangi bir sayısal değişmez değeri veya sayısal değişmez değeri veya NaN veya Infynity içeren bir değişken ) === 'sayı'

typeof true (veya false veya true veya false içeren bir değişken ) === 'boolean'

typeof undefined (veya undefined değişken veya undefined içeren değişken ) === 'undefined'

Dize , sayı ve boolean veri türleri olarak hem de temsil edilebilir Nesneler ve Sigara nesneleri onların typeof hep === 'nesne' olduğu nesneler olarak temsil edilir .her. Nesne veri türlerini anladıktan sonra buna geri döneceğiz.

Nesneler

Nesne veri tipleri ayrıca iki türe ayrılabilir

  1. İşlev türü nesneleri
  2. İşlev Dışı türdeki nesneler

Fonksiyon tipi nesneler dize döndürür olanlardır 'fonksiyonunu' ile typeof operatörü. Kullanıcı tarafından tanımlanan tüm işlevler ve yeni işleç kullanarak yeni nesneler oluşturabilen nesnelerdeki tüm JavaScriptler bu kategoriye girer. Örneğin.

  • Nesne
  • sicim
  • Numara
  • Boole
  • Dizi
  • Yazılan Diziler
  • RegExp
  • fonksiyon
  • Yeni işleç kullanarak yeni nesneler oluşturabilen diğer tüm yerleşik nesneler
  • function UserDefinedFunction () {/ * kullanıcı tanımlı kod * /}

Yani, typeof (Object) === typeof (String) === typeof (Number) === typeof (Boolean) === typeof (Array) === typeof (RegExp) === typeof (İşlev) == = typeof (UserDefinedFunction) === 'işlev'

Tüm İşlev türü nesneleri aslında yerleşik JavaScript nesnesi İşlevinin örnekleridir ( İşlev nesnesi dahil, yani özyinelemeli olarak tanımlanır). Sanki bu nesneler şu şekilde tanımlanmış gibi

var Object= new Function ([native code for object Object])
var String= new Function ([native code for object String])
var Number= new Function ([native code for object Number])
var Boolean= new Function ([native code for object Boolean])
var Array= new Function ([native code for object Array])
var RegExp= new Function ([native code for object RegExp])
var Function= new Function ([native code  for object Function])
var UserDefinedFunction= new Function ("user defined code")

Belirtildiği gibi, İşlev türü nesneleri yeni işleci kullanarak yeni nesneler de oluşturabilir . Örneğin Object , String , Number , Boolean , Array , RegExp veya UserDefinedFunction türünde bir nesne kullanılarak oluşturulabilir.

var a=new Object() or var a=Object() or var a={} //Create object of type Object
var a=new String() //Create object of type String
var a=new Number() //Create object of type Number
var a=new Boolean() //Create object of type Boolean
var a=new Array() or var a=Array() or var a=[]  //Create object of type Array
var a=new RegExp() or var a=RegExp() //Create object of type RegExp
var a=new UserDefinedFunction() 

Bu şekilde oluşturulan nesnelerin tümü İşlevsiz türdeki nesnelerdir ve typeof === 'object' öğelerini döndürür . Tüm bu durumlarda "a" nesnesi yeni işleci kullanarak nesne oluşturamaz. Yani aşağıdakiler yanlış

var b=new a() //error. a is not typeof==='function'

Nesnenin inşa Matematik olduğu typeof === 'nesne' . Bu nedenle yeni bir operatör tarafından Math türünde yeni bir nesne oluşturulamaz.

var b=new Math() //error. Math is not typeof==='function'

Ayrıca Object , Array ve RegExp işlevlerinin new operatörünü kullanmadan yeni bir nesne oluşturabileceğini unutmayın . Ancak şu uçanlar değil.

var a=String() // Create a new Non Object string. returns a typeof==='string' 
var a=Number() // Create a new Non Object Number. returns a typeof==='number'
var a=Boolean() //Create a new Non Object Boolean. returns a typeof==='boolean'

Kullanıcı tanımlı fonksiyonlar özel durumdur.

var a=UserDefinedFunction() //may or may not create an object of type UserDefinedFunction() based on how it is defined.

Yana Fonksiyon tipi nesneleri yeni nesneler oluşturabilir onlar da denir Yapıcılar .

Her Yapıcı / İşlev (ister yerleşik ister kullanıcı tanımlı olsun) otomatik olarak tanımlandığında , varsayılan olarak değeri nesne olarak ayarlanan "prototip" adlı bir özelliğe sahiptir . Bu nesnenin kendisi, "Yapıcı" adlı bir özelliğe sahiptir ve varsayılan olarak Yapıcı / İşlev'e geri başvurur .

Örneğin bir işlev tanımladığımızda

function UserDefinedFunction()
{
}

takip otomatik olarak gerçekleşir

UserDefinedFunction.prototype={constructor:UserDefinedFunction}

Bu "prototip" özelliği yalnızca İşlev türü nesnelerinde bulunur (ve hiçbir zaman İşlev Dışı türü nesnelerde ).

Bunun nedeni , yeni bir nesne oluşturulduğunda (yeni işleç kullanılarak), tüm özellikleri ve yöntemleri Yapıcı işlevinin geçerli prototip nesnesinden, yani bir iç referanstan devralır , yeni oluşturulan nesnede, Oluşturucu işlevinin geçerli prototip nesnesi tarafından başvurulan nesneye başvuran oluşturulur.

Nesnede devralınan özelliklere başvuruda bulunmak için oluşturulan bu "iç başvuru" , nesnenin prototipi olarak bilinir ( Yapıcı'nın "prototip" özelliği tarafından başvurulan nesneye başvurur, ancak bundan farklıdır). Herhangi bir nesne (İşlev veya İşlevsiz ) için bu, Object.getPrototypeOf () yöntemi kullanılarak alınabilir . Bu yöntemi kullanarak, bir nesnenin prototip zincirini izleyebilirsiniz.

Ayrıca, oluşturulan her nesne ( Fonksiyon tipi veya Olmayan Fonksiyon Tipi ) bir sahiptir "kurucu" Yapıcı işlevinin prototip özelliği tarafından başvurulan nesneden devralınan özelliğini. Varsayılan olarak bu "yapıcı" özelliği, onu oluşturan Yapıcı işlevine başvurur ( Yapıcı İşlevinin varsayılan "prototipi" değiştirilmezse).

Tüm İşlev türü nesneler için yapıcı işlevi her zaman işlevdir İşlev () {}

For Non Fonksiyon tipi nesneleri (Matematik nesnesi Dahili örneğin JavaScript) yapıcı işlevi onu oluşturan fonksiyondur. İçin matematik nesne bunun fonksiyonu nesnesi () {} .

Yukarıda açıklanan tüm kavramlar destekleyici bir kod olmadan anlaşılması biraz zor olabilir. Kavramı anlamak için lütfen aşağıdaki kod satır satır gidin. Daha iyi bir anlayışa sahip olmak için onu uygulamaya çalışın.

function UserDefinedFunction()
{ 

} 

/* creating the above function automatically does the following as mentioned earlier

UserDefinedFunction.prototype={constructor:UserDefinedFunction}

*/


var newObj_1=new UserDefinedFunction()

alert(Object.getPrototypeOf(newObj_1)===UserDefinedFunction.prototype)  //Displays true

alert(newObj_1.constructor) //Displays function UserDefinedFunction

//Create a new property in UserDefinedFunction.prototype object

UserDefinedFunction.prototype.TestProperty="test"

alert(newObj_1.TestProperty) //Displays "test"

alert(Object.getPrototypeOf(newObj_1).TestProperty)// Displays "test"

//Create a new Object

var objA = {
        property1 : "Property1",
        constructor:Array

}


//assign a new object to UserDefinedFunction.prototype
UserDefinedFunction.prototype=objA

alert(Object.getPrototypeOf(newObj_1)===UserDefinedFunction.prototype)  //Displays false. The object referenced by UserDefinedFunction.prototype has changed

//The internal reference does not change
alert(newObj_1.constructor) // This shall still Display function UserDefinedFunction

alert(newObj_1.TestProperty) //This shall still Display "test" 

alert(Object.getPrototypeOf(newObj_1).TestProperty) //This shall still Display "test"


//Create another object of type UserDefinedFunction
var newObj_2= new UserDefinedFunction();

alert(Object.getPrototypeOf(newObj_2)===objA) //Displays true.

alert(newObj_2.constructor) //Displays function Array()

alert(newObj_2.property1) //Displays "Property1"

alert(Object.getPrototypeOf(newObj_2).property1) //Displays "Property1"

//Create a new property in objA
objA.property2="property2"

alert(objA.property2) //Displays "Property2"

alert(UserDefinedFunction.prototype.property2) //Displays "Property2"

alert(newObj_2.property2) // Displays Property2

alert(Object.getPrototypeOf(newObj_2).property2) //Displays  "Property2"

Her nesnenin prototip zinciri nihayetinde Object.prototype'e (kendisinde herhangi bir prototip nesnesi olmayan) kadar uzanır. Bir nesnenin prototip zincirini izlemek için aşağıdaki kod kullanılabilir

var o=Starting object;

do {
    alert(o + "\n" + Object.getOwnPropertyNames(o))

}while(o=Object.getPrototypeOf(o))

Çeşitli nesneler için prototip zinciri aşağıdaki gibi çalışır.

  • Her Function nesnesi (yerleşik Function nesnesi dahil) -> Function.prototype -> Object.prototype -> null
  • Basit Nesneler (yeni Object tarafından oluşturuldu () veya Math nesnesine yerleşik olarak dahil {}) -> Object.prototype -> null
  • New veya Object.create ile oluşturulan nesne -> Bir veya daha fazla prototip zinciri -> Object.prototype -> null

Herhangi bir prototip içermeyen bir nesne oluşturmak için aşağıdakileri kullanın:

var o=Object.create(null)
alert(Object.getPrototypeOf(o)) //Displays null

Yapıcının prototip özelliğinin null değerine ayarlanmasının, boş bir prototip içeren bir nesne yaratacağı düşünülebilir. Ancak bu gibi durumlarda yeni oluşturulan nesnenin prototipi Object.prototype olarak ve yapıcısı Object fonksiyonuna ayarlanır. Bu, aşağıdaki kodla gösterilmiştir

function UserDefinedFunction(){}
UserDefinedFunction.prototype=null// Can be set to any non object value (number,string,undefined etc.)

var o=new UserDefinedFunction()
alert(Object.getPrototypeOf(o)==Object.prototype)   //Displays true
alert(o.constructor)    //Displays Function Object

Bu makalenin özetinde aşağıdaki

  • İki tür nesne vardır: İşlev türleri ve İşlevsiz türler
  • Yalnızca İşlev türü nesneleri yeni işleci kullanılarak yeni bir nesne oluşturabilir . Bu şekilde oluşturulan nesneler İşlevsiz tip nesnelerdir. Sigara Fonksiyon tipi nesneler ayrıca kullanan bir nesne oluşturamıyor yeni operatörünü .

  • Tüm İşlev türü nesnelerin varsayılan olarak "prototip" özelliği vardır. Bu "prototip" özelliği , varsayılan olarak İşlev türü nesnesinin kendisine başvuran "yapıcı" özelliğine sahip bir nesneye başvurur.

  • Tüm nesneler ( İşlev türü ve İşlev Dışı tür ), varsayılan olarak onu oluşturan İşlev türü nesnesine / Yapıcısına başvuran bir "yapıcı" özelliğine sahiptir .

  • Oluşturulan her nesne, onu oluşturan Oluşturucu'nun "prototip" özelliği tarafından başvurulan nesneye dahili olarak başvurur . Bu nesne, oluşturulan nesnenin prototipi olarak bilinir (bu, başvurduğu İşlev türü nesneleri "prototip" özelliğinden farklıdır). Bu şekilde oluşturulan nesne, Yapıcı'nın "prototip" özelliği (nesne oluşturma sırasında) tarafından başvurulan nesnede tanımlanan yöntemlere ve özelliklere doğrudan erişebilir.

  • Bir nesnenin prototipi (ve dolayısıyla devralınan özellik adları) Object.getPrototypeOf () yöntemi kullanılarak alınabilir . Aslında bu yöntem, nesnenin tüm prototip zincirinde gezinmek için kullanılabilir.

  • Her nesnenin prototip zinciri nihayetinde Object.prototype'e geri döner (Nesne Object.create (null) kullanılarak oluşturulmadıkça, bu durumda nesnenin prototipi olmaz).

  • typeof (new Array ()) === 'nesne' dil tasarımı gereği, Douglas Crockford'un işaret ettiği gibi bir hata değil

  • Yapıcı'nın prototip özelliğinin null (veya tanımsız, number, true, false, string) olarak ayarlanması, null prototipine sahip bir nesne oluşturmaz. Bu gibi durumlarda, yeni oluşturulan nesnenin prototipi Object.prototype olarak ve yapıcısı Object fonksiyonuna ayarlanır.

Bu yardımcı olur umarım.


24

prototypalKalıtım kavramı birçok geliştirici için en karmaşık olanlardan biridir. prototypal inheritanceDaha iyi anlamak için sorunun kökenini anlamaya çalışalım . Bir plainişlevle başlayalım .

resim açıklamasını buraya girin

newÜzerinde bir operatör kullanırsak Tree function, constructorişlev olarak adlandırırız .

resim açıklamasını buraya girin

Her JavaScriptişlevin bir prototype. Giriş Tree.prototypeyaptığınızda ...

resim açıklamasını buraya girin

Yukarıdaki console.log()çıktıya bakarsanız, bir yapıcı özelliği Tree.prototypeve bir __proto__özellik de görebilirsiniz. __proto__Temsil prototypeBu o functionkapalı dayanır ve bu sadece düz bir yana olduğu JavaScript functionhiçbir ile inheritancehenüz kurmak, bu atıfta Object prototypehangi sadece JavaScript yerleşik bir şeydir ...

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/prototype

Bunun gibi şeyler var .toString, .toValue, .hasOwnProperty...

__proto__Bu benim mozilla getirildi ve yerine Object.getPrototypeOfalmak için yöntem ile değiştirilir object's prototype.

resim açıklamasını buraya girin

Object.getPrototypeOf(Tree.prototype); // Object {} 

Haydi bir yöntem ekleyelim Tree prototype.

resim açıklamasını buraya girin

Biz değiştirdik Rootve bir functionşube ekledik .

resim açıklamasını buraya girin

Bu, bir instanceoluşturduğunuzda Tree, branchyöntemini çağırabileceğiniz anlamına gelir .

resim açıklamasını buraya girin

Ayrıca ekleyebilir primitivesveya objectsbizim Prototype.

resim açıklamasını buraya girin

En bir ekleyelim child-treekızımız için Tree.

resim açıklamasını buraya girin

Burada ChildTree'den miras alır, prototypeburada yaptığımız Object.create()şey, geçtiğiniz şeylere dayanan yeni bir nesne oluşturmak için yöntem kullanmaktır Tree.prototype. Bu durumda yaptığımız şey, Child'ın prototipini prototiple aynı görünen yeni bir nesneye ayarlamaktır Tree. Sonra ayarlıyoruz Child's constructor to Child, eğer yapmazsak işaret eder Tree().

resim açıklamasını buraya girin

Childartık kendi vardır prototype, onun __proto__için puan Treeve Tree's prototypetabana noktaları Object.

Child  
|
 \
  \
   Tree.prototype
   - branch
   |
   |
    \
     \
      Object.prototype
      -toString
      -valueOf
      -etc., etc.

Şimdi başlangıçta kullanılabilir olan bir instanceof Childve call brancholuşturursunuz Tree. Biz aslında tanımlamadınız branchüzerinde Child prototype. ANCAK Root prototypeÇocuk hangi miras.

resim açıklamasını buraya girin

JS'de her şey bir nesne değildir, her şey bir nesne gibi davranabilir.

Javascriptstrings, number, booleans, undefined, null.Onlar gibi ilkellere sahip değiller object(i.e reference types), ama kesinlikle bir gibi davranabilirler object. Burada bir örneğe bakalım.

resim açıklamasını buraya girin

Bu girişin ilk satırında, primitiveada bir dize değeri atanır. İkinci satır ismi a gibi davranır objectve charAt(0)nokta gösterimi kullanarak çağırır .

Perde arkasında olan budur: // JavaScriptmotorun yaptığı

resim açıklamasını buraya girin

String objectO (adı verilen bir işlem yok edilmeden önce yalnızca için bir ifade olduğunu autoboxing). Tekrar geri dönelim prototypal inheritance.

  • Javascriptdelegationdayalı olarak kalıtım destekler prototypes.
  • Her Functionbirinin prototypebaşka bir nesneye gönderme yapan bir özelliği vardır .
  • properties/functionsyoksa objectkendisinden veya prototypezincir yoluyla bakılırsa

prototypeJS'de A , bir yieldsbaşkasının üst öğesine gittiğiniz bir nesnedir object. [yani .. delegasyon] Delegation , bir şey yapamazsanız, başka birine bunu sizin için yapmasını söyleyeceğiniz anlamına gelir.

resim açıklamasını buraya girin

https://jsfiddle.net/say0tzpL/1/

Yukarıdaki kemanı ararsanız, köpek toStringyönteme erişebilir , ancak içinde mevcut değildir, ancak delege olan prototip zinciri aracılığıyla kullanılabilirObject.prototype

resim açıklamasını buraya girin

Aşağıdakine bakarsanız, her birinde callmevcut olan yönteme erişmeye çalışıyoruz function.

resim açıklamasını buraya girin

https://jsfiddle.net/rknffckc/

Yukarıdaki kemanı ararsanız, Profileİşlev callyönteme erişebilir , ancak onun içinde mevcut değildir, ancak delege olan prototip zinciri aracılığıyla kullanılabilirFunction.prototype

resim açıklamasını buraya girin

Not: prototype fonksiyon yapıcısının __proto__bir özelliğidir , oysa fonksiyon yapıcısından inşa edilen nesnelerin bir özelliğidir. Her işlev prototype, değeri boş olan bir özellik ile birlikte gelir object. Fonksiyonun bir örneğini oluşturduğumuzda, dahili bir özellik elde ederiz [[Prototype]]veya __proto__referansı Fonksiyonun prototipi olur constructor.

resim açıklamasını buraya girin

Yukarıdaki şema biraz karmaşık görünüyor, ancak nasıl prototype chainingçalıştığına dair tüm resmi ortaya koyuyor. Bunu yavaşça inceleyelim:

İki örneğidir b1ve b2olan yapıcı, Barve üst Foo ve prototip zincirinden iki yöntem vardır identifyve speaküzeri BarveFoo

resim açıklamasını buraya girin

https://jsfiddle.net/kbp7jr7n/

Yukarıdaki kodu bakarsak sahip Fooyöntemi vardır yapıcı identify()ve Barsahip yapıcı speakyöntemini. Biz iki oluşturmak Barörneği b1ve b2kimin ebeveyn türüdür Foo. Şimdi çağırma speakyöntemi ile Barkonuşmayı kimin aradığını prototypezincir aracılığıyla tanımlayabiliyoruz .

resim açıklamasını buraya girin

Barşimdi tüm yöntemleri Fookendi içinde tanımlanmıştır prototype. En anlamakta daha da dalalım Object.prototypeve Function.prototypeve nasıl ilişkili. Eğer yapıcısı yukarı bakacak olursak Foo, Barve Objectvardır Function constructor.

resim açıklamasını buraya girin

prototypeAit Barolduğu Foo, prototypebir FooIS Objectve yakından bakarsanız prototypeait Fooilgilidir Object.prototype.

resim açıklamasını buraya girin

Bunu kapatmadan önce, yukarıdaki her şeyi özetlemek için buraya küçük bir kod parçası ekleyelim . Burada instanceofoperatörün object, prototypezincirinde bir prototypeözelliğinin constructortüm büyük diyagramı özetleyip özetlemediğini kontrol etmek için kullanıyoruz .

resim açıklamasını buraya girin

Umarım bu eklenti bazı bilgiler, bu tür kavramak için büyük olabilir biliyorum ... basit bir deyişle onun sadece nesnelere bağlı nesneler !!!!


22

Bu ".prototype" özelliğinin tam amacı nedir?

Standart sınıflara arayüz genişletilebilir hale gelir. Örneğin, Arraysınıfı kullanıyorsunuz ve ayrıca tüm dizi nesneleriniz için özel bir serileştirici eklemeniz gerekiyor. Bir alt sınıfı kodlamak için zaman harcıyor musunuz, yoksa kompozisyon veya ... prototype özelliği, kullanıcıların bir sınıf için mevcut üye / yöntem grubunu tam olarak kontrol etmesine izin vererek bunu çözer.

Prototipleri ekstra vtable-pointer olarak düşünün. Orijinal sınıfta bazı üyeler eksik olduğunda, prototip çalışma zamanında aranır.


21

Prototip zincirlerini iki kategoriye ayırmak yardımcı olabilir.

Yapıcıyı düşünün:

 function Person() {}

Değeri Object.getPrototypeOf(Person)bir işlevdir. Aslında öyle Function.prototype. PersonBir işlev olarak oluşturulduğundan beri , tüm işlevlerin sahip olduğu aynı prototip işlev nesnesini paylaşır. Aynıdır Person.__proto__, ancak bu özellik kullanılmamalıdır. Her neyse, Object.getPrototypeOf(Person)sizinle prototip zinciri denilen merdivenden etkili bir şekilde yürüyün.

Yukarı yönde zincir şöyle görünür:

    PersonFunction.prototypeObject.prototype(bitiş noktası)

Önemli Bu prototip zinciri nesnelerle ilgisi olmasıdır Personedebilirsiniz inşa . Bu inşa edilen nesnelerin kendi prototip zincirleri vardır ve bu zincirin potansiyel olarak yukarıda belirtilenle ortak yakın atası olmayabilir.

Örneğin bu nesneyi ele alalım:

var p = new Person();

p'nin Kişi ile doğrudan prototip-zincir ilişkisi yoktur . İlişkileri farklıdır. P nesnesinin kendi prototip zinciri vardır. Kullanarak Object.getPrototypeOf, zinciri aşağıdaki gibi bulacaksınız:

    pPerson.prototypeObject.prototype(bitiş noktası)

Bu zincirde herhangi bir işlev nesnesi yoktur (bu olmasına rağmen).

Yani Personkendi yaşamlarını yaşayan iki tür zincirle ilgili gibi görünüyor. Bir zincirden diğerine "atlamak" için şunları kullanırsınız:

  1. .prototype: yapıcı zincirinden yaratılan nesnenin zincirine atlayın. Bu özellik bu nedenle yalnızca işlev nesneleri için tanımlanır ( newyalnızca işlevlerde kullanılabilir).

  2. .constructor: oluşturulan nesnenin zincirinden kurucu zincirine atlayın.

Aşağıda, sütunlar olarak gösterilen iki prototip zincirinin görsel bir sunumu verilmiştir:

resim açıklamasını buraya girin

Özetle:

prototypeMülkiyet hiçbir bilgi veren kişinin ancak nesnelerin, prototip zincirinde yarattığı konu.

Mülkün adının prototypekarışıklığa yol açması şaşırtıcı değildir . Bu mülkün adı prototypeOfConstructedInstancesveya bu satır boyunca bir şey olsaydı daha açık olurdu .

İki prototip zinciri arasında ileri geri atlayabilirsiniz:

Person.prototype.constructor === Person

Bu simetri, açık bir şekilde farklı bir nesne atayarak kırılabilir. prototype mülke (daha sonra bunun hakkında daha fazla bilgi).

Bir İşlev Oluşturun, İki Nesne Alın

Person.prototype, işlevin Personoluşturulduğu anda oluşturulan bir nesnedir . Bu Personyapıcı henüz yürütmediyse de yapıcı olarak vardır . Böylece iki nesne aynı anda oluşturulur:

  1. Fonksiyonun Personkendisi
  2. İşlev yapıcı olarak çağrıldığında prototip görevi görecek nesne

Her ikisi de nesnedir, ancak farklı rolleri vardır: işlev nesnesi oluşturulur , diğer nesne ise işlevi oluşturacak herhangi bir nesnenin prototipini temsil eder. Prototip nesnesi, prototip zincirinde oluşturulmuş nesnenin üst öğesi olur.

Bir fonksiyon aynı zamanda bir nesne olduğundan, kendi prototip zincirinde kendi ebeveyni de vardır, ancak bu iki zincirin farklı şeyler hakkında olduğunu hatırlayın.

Sorunu kavramaya yardımcı olabilecek bazı eşitlikler - tüm bu baskılar true:

function Person() {};

// This is prototype chain info for the constructor (the function object):
console.log(Object.getPrototypeOf(Person) === Function.prototype);
// Step further up in the same hierarchy:
console.log(Object.getPrototypeOf(Function.prototype) === Object.prototype);
console.log(Object.getPrototypeOf(Object.prototype) === null);
console.log(Person.__proto__ === Function.prototype);
// Here we swap lanes, and look at the constructor of the constructor
console.log(Person.constructor === Function);
console.log(Person instanceof Function);

// Person.prototype was created by Person (at the time of its creation)
// Here we swap lanes back and forth:
console.log(Person.prototype.constructor === Person);
// Although it is not an instance of it:
console.log(!(Person.prototype instanceof Person));
// Instances are objects created by the constructor:
var p = new Person();
// Similarly to what was shown for the constructor, here we have
// the same for the object created by the constructor:
console.log(Object.getPrototypeOf(p) === Person.prototype);
console.log(p.__proto__ === Person.prototype);
// Here we swap lanes, and look at the constructor
console.log(p.constructor === Person);
console.log(p instanceof Person);

Prototip zincirine seviye ekleme

Bir yapıcı işlevi oluşturduğunuzda bir prototip nesnesi oluşturulmuş olsa da, o nesneyi yok sayabilir ve bu kurucu tarafından oluşturulan sonraki örnekler için prototip olarak kullanılması gereken başka bir nesne atayabilirsiniz.

Örneğin:

function Thief() { }
var p = new Person();
Thief.prototype = p; // this determines the prototype for any new Thief objects:
var t = new Thief();

Şimdi t'nin prototip zinciri p'den bir adım daha uzundur :

    tpPerson.prototypeObject.prototype(bitiş noktası)

Diğer prototip zinciri artık uzun değil: Thiefve Personprototip zincirlerinde aynı ebeveyni paylaşan kardeşler:

    Person}
    Thief  } → Function.prototypeObject.prototype(bitiş noktası)

Daha önce sunulan grafik daha sonra buna genişletilebilir (orijinal Thief.prototypedışarıda bırakılır):

resim açıklamasını buraya girin

Mavi çizgiler prototip zincirlerini, diğer renkli çizgiler diğer ilişkileri temsil eder:

  • bir nesne ve onun kurucusu arasında
  • bir kurucu ile nesneleri oluşturmak için kullanılacak prototip nesne arasında


16

obj_n.prop_XReferans verildiğinde "prototip zinciri" özyinelemeli kural olarak açıklamak için yararlı buldum :

eğer obj_n.prop_Xyoksa, çek obj_n+1.prop_Xnereyeobj_n+1 = obj_n.[[prototype]]

Eğer prop_Xnihayet ardından k-inci prototip nesnesine bulunur

obj_1.prop_X = obj_1.[[prototype]].[[prototype]]..(k-times)..[[prototype]].prop_X

Javascript nesnelerinin özelliklerine göre ilişkisinin bir grafiğini burada bulabilirsiniz:

js nesneleri grafiği

http://jsobjects.org


14

Bir kurucu bir nesne oluşturduğunda, o nesne, özellik referanslarını çözümlemek amacıyla kurucunun “prototip” özelliğine örtük olarak başvurur. Yapıcı "prototip" özelliğine program ifade constructor.prototype tarafından başvurulabilir ve bir nesnenin prototipine eklenen özellikler miras yoluyla, prototipi paylaşan tüm nesneler tarafından paylaşılır.


11

Burada açıklanması gereken iki ayrı ama ilgili varlık var:

  • .prototypeFonksiyonların özelliği.
  • [[Prototype]][1] tüm nesnelerin özelliği [2] .

Bunlar iki farklı şey.

[[Prototype]]özelliği:

Bu, tüm [2] nesnelerde bulunan bir özelliktir .

Burada depolanan şey, bir nesnenin kendisi [[Prototype]]olarak başka bir nesneye işaret eden kendine ait bir başka nesnedir. Bu diğer nesnenin [[Prototype]]kendine has bir özelliği var. Bu hikaye, tüm nesnelerde (örneğin .toString) erişilebilir yöntemler sağlayan prototip nesneye ulaşana kadar devam eder .

[[Prototype]]Mülkiyet oluşturan şeyin parçasıdır [[Prototype]]zinciri. Bu [[Prototype]]nesne zinciri, örneğin [[Get]]veya [[Set]]bir nesne üzerinde işlemler gerçekleştirildiğinde incelenen şeydir :

var obj = {}
obj.a         // [[Get]] consults prototype chain
obj.b = 20    // [[Set]] consults prototype chain

.prototypeözelliği:

Bu, yalnızca işlevlerde bulunan bir özelliktir. Çok basit bir işlev kullanma:

function Bar(){};

.prototypeMülkiyet bir nesneyi tutar atanacak b.[[Prototype]]bunu yaptığında var b = new Bar. Bunu kolayca inceleyebilirsiniz:

// Both assign Bar.prototype to b1/b2[[Prototype]]
var b = new Bar;
// Object.getPrototypeOf grabs the objects [[Prototype]]
console.log(Object.getPrototypeOf(b) === Bar.prototype) // true

En önemlilerinden biri .prototypes olmasıdır ait Objectfonksiyonu . Bu prototip, tüm [[Prototype]]zincirlerin içerdiği prototip nesneyi içerir. Üzerinde, yeni nesneler için mevcut tüm yöntemler tanımlanır:

// Get properties that are defined on this object
console.log(Object.getOwnPropertyDescriptors(Object.prototype))

Şimdi, .prototypebir nesne olduğu için bir [[Prototype]]özelliği var. Herhangi bir atama yapmadığınızda Function.prototype, .prototype'ın [[Prototype]]prototip nesneyi (Object.prototype ) işaret eder. Bu, yeni bir işlev oluşturduğunuzda otomatik olarak gerçekleştirilir.

Bu şekilde, new Bar;prototip zincirini her yaptığınız zaman sizin için ayarlanmış, her şeyi Bar.prototypeve her şeyi tanımlamış olursunuz Object.prototype:

var b = new Bar;
// Get all Bar.prototype properties
console.log(b.__proto__ === Bar.prototype)
// Get all Object.prototype properties
console.log(b.__proto__.__proto__ === Object.prototype)

Yaptığınız herFunction.prototype şeye atama yaptığınızda, prototip zincirini başka bir nesne içerecek şekilde genişletiyorsunuz. Tek başına bağlantılı bir listeye ekleme gibi.

Bu temel olarak , işlev tarafından oluşturulan herhangi bir nesne tarafından [[Prototype]]atanmak üzere atanan nesne üzerinde tanımlanan özelliklere izin veren zinciri değiştirir Function.prototype.


[1: Bu kimseyi karıştırmayacak; aracılığıyla sunulan mal birçok uygulamalarda. __proto__
[2]: Hepsi hariç null.


10

Size prototip anlayışımı anlatayım. Burada kalıtım ile diğer dilleri karşılaştıramayacağım. İnsanların dilleri karşılaştırmayı bırakmasını ve sadece dili kendisi olarak anlamasını diliyorum. Prototipleri ve prototip kalıtımını anlamak o kadar basit ki, aşağıda size göstereceğim.

Prototip, ürün oluşturduğunuz bir model gibidir. Anlaşılması gereken önemli nokta, prototip olarak başka bir nesneyi kullanarak bir nesne oluşturduğunuzda, prototip ile ürün arasındaki bağlantının kalıcı olmasıdır. Örneğin:

var model = {x:2};
var product = Object.create(model);
model.y = 5;
product.y
=>5

Her nesne, Object.getPrototypeOf()işlev tarafından erişilebilen [[prototip]] adlı dahili bir özellik içerir . Object.create(model)yeni bir nesne oluşturur ve onun [[prototip]] özelliğini nesne modeline ayarlar . Böylece yaptığınızda Object.getPrototypeOf(product), nesne modelini alacaksınız .

Properties ürünün şu şekilde ele alınır:

  • Bir mülke sadece değerini okumak için erişildiğinde, kapsam zincirinde aranır. Değişkenin aranması üründen prototipine doğru başlar . Aramada böyle bir değişken bulunursa, arama burada durdurulur ve değer döndürülür. Kapsam zincirinde böyle bir değişken bulunamazsa tanımsız döndürülür.
  • Bir özellik yazıldığında (değiştirildiğinde), özellik her zaman ürün nesnesi üzerine yazılır . Eğer ürün zaten böyle bir özelliğe sahip değildir, bu örtük oluşturulur ve yazılır.

Prototip özelliğini kullanarak nesnelerin bu şekilde bağlanması prototip palet mirası olarak adlandırılır. Orada, çok basit, katılıyorum?


Her zaman ürün üzerinde yazılmaz. Örneğe özgü üyelerin başlatılması ve paylaşılan üyelerin prototip üzerinde çalışabileceğini çok netleştirmiyorsunuz. Özellikle örneğe özgü değişken üyeleriniz varsa: stackoverflow.com/questions/16063394/…
HMR

HMR: Cevabınızdaki örnekte ben.food.push ("Hamburger"); satırı, prototip nesnesinin özelliğini aşağıdakilerden dolayı değiştirir: 1.) Önce ben.food aranır ve herhangi bir arama eylemi yalnızca kapsam zincirini arar. 2.) Bu ben.food nesnesinin itme işlevi yürütülür. Cevabımda mod yazarak, açık bir şekilde bir değer ayarladığınızda, yani ben.food = ['Idly']; Bu, her zaman ürün nesnesinde yeni bir özellik (zaten yoksa) oluşturur ve sonra değeri ona atar.
Aravind

HMR: Yorumunuz için teşekkür ederim, düşünmem ve anlayışımı test etmemi sağladı.
Aravind

Ben.food yeniden atanırken, Object.defineProperty, Object.defineProperties veya Object.create ikinci argümanla oluşturulmadığı sürece (her zaman değil) yiyecek elemanının gölgesini oluşturur. Bir alıcı ayarlayıcı oluşturduğunuzda, yeniden atama ile prototipi bile (neye benziyor) değiştirebilirsiniz. Miras kalıpları söz konusu olduğunda, yapıcı işlevinin anlaşılması zor olduğunu ve bazı büyük problemleri olduğunu anlıyorum, ancak anlarsanız iyi olur. JavaScript'te devralma bir prototip ayarlamayla başlamaz ve bitmez, ilklendirmeler (yapıcılar) da (yeniden) kullanılacaktır.
HMR

Cevabınız prototipi açıklamakta iyidir, ancak JavaScript ve örneğe özgü üyelerde kalıtımın basitleştirilmesiyle yanlış yorumlanabilir. Bir örnek üzerinde bir prototip üyesini değiştirmenin neden diğer örnekleri etkilediğine dair birçok soru soruldu.
HMR


10

Aşağıdaki keyValueStorenesneyi düşünün :

var keyValueStore = (function() {
    var count = 0;
    var kvs = function() {
        count++;
        this.data = {};
        this.get = function(key) { return this.data[key]; };
        this.set = function(key, value) { this.data[key] = value; };
        this.delete = function(key) { delete this.data[key]; };
        this.getLength = function() {
            var l = 0;
            for (p in this.data) l++;
            return l;
        }
    };

    return  { // Singleton public properties
        'create' : function() { return new kvs(); },
        'count' : function() { return count; }
    };
})();

Bunu yaparak bu nesnenin yeni bir örneğini oluşturabilirsiniz:

kvs = keyValueStore.create();

Bu nesnenin her örneği aşağıdaki genel özelliklere sahip olacaktır:

  • data
  • get
  • set
  • delete
  • getLength

Şimdi, bu keyValueStorenesnenin 100 örneğini oluşturduğumuzu varsayalım . Olsa get, set, delete, getLengthBu 100 örneklerinin her biri için aynı şeyi yapacağız, her örnek bu işlevin kendi kopyası vardır.

Şimdi, sadece tek bir olabilir eğer hayal get, set, deleteve getLengthkopyalama ve her bir örnek aynı işlevi başvurmak. Bu performans için daha iyi olur ve daha az bellek gerektirir.

Prototiplerin devreye girdiği yer burasıdır. Prototip, miras alınan ancak örnekler tarafından kopyalanmayan özelliklerin "planı" dır. Yani bu, bir nesnenin tüm örnekleri için bellekte yalnızca bir kez var olduğu ve tüm bu örnekler tarafından paylaşıldığı anlamına gelir.

Şimdi keyValueStorenesneyi tekrar düşünün . Bu şekilde yeniden yazabilirim:

var keyValueStore = (function() {
    var count = 0;
    var kvs = function() {
        count++;
        this.data = {};
    };

    kvs.prototype = {
        'get' : function(key) { return this.data[key]; },
        'set' : function(key, value) { this.data[key] = value; },
        'delete' : function(key) { delete this.data[key]; },
        'getLength' : function() {
            var l = 0;
            for (p in this.data) l++;
            return l;
        }
    };

    return  {
        'create' : function() { return new kvs(); },
        'count' : function() { return count; }
    };
})();

Bu, keyValueStoretüm yöntemlerinin artık bir prototipe yerleştirilmesi dışında , nesnenin önceki sürümüyle tam olarak aynıdır . Bunun anlamı, 100 örneğin hepsinin artık her biri kendi kopyasına sahip olmak yerine bu dört yöntemi paylaşmasıdır.


9

Özet:

  • Fonksiyonlar javascript içindeki nesnelerdir ve bu nedenle özellikleri olabilir
  • (Yapıcı) işlevlerinin her zaman bir prototip özelliği vardır
  • newAnahtar kelime ile bir yapıcı olarak bir işlev kullanıldığında , nesne prototip alır. Bu prototipe bir başvuru __proto__, yeni oluşturulan nesnenin özelliğinde bulunabilir .
  • Bu __proto__özellik prototype, yapıcı işlevinin özelliğine karşılık gelir .

Misal:

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

let me = new Person('willem');

console.log(Person.prototype) // Person has a prototype property

console.log(Person.prototype === me.__proto__) // the __proto__ property of the instance refers to prototype property of the function.

Bu neden faydalı?

Javascript, 'prototypal mirası' olarak adlandırılan Nesnelerdeki özellikleri ararken bir mekanizmaya sahiptir , temel olarak aşağıdakileri yapar:

  • İlk olarak, özellik Nesnenin kendisinde bulunuyorsa kontrol edilir. Öyleyse bu özellik döndürülür.
  • Mülk nesnenin kendisinde bulunmuyorsa, 'protokolü tırmanacaktır'. Temelde proto özelliği tarafından atıfta bulunulan nesneye bakar . Orada özelliğin proto tarafından belirtilen nesne üzerinde kullanılabilir olup olmadığını kontrol eder.
  • Özellik proto nesnesinde yer almıyorsa , proto zincirini Object nesnesine kadar tırmanacaktır .
  • Özelliği nesnenin ve prototip zincirinin hiçbir yerinde bulamazsa tanımsız olarak geri döner.

Örneğin:

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

let mySelf = new Person('Willem');

console.log(mySelf.__proto__ === Person.prototype);

console.log(mySelf.__proto__.__proto__ === Object.prototype);

Güncelleme:

__proto__Çoğu modern tarayıcılarda olacağını prototip nesne başvurusu elde etmek daha iyi bir yol uygulanır rağmen mülkiyet, kullanımdan kaldırıldı:

Object.getPrototypeOf()


7

Bu tür şeyleri anlamaya geldiğinde her zaman benzetmeleri severim. Prototipler çok daha basit bir paradigma olmasına rağmen, 'prototipsel miras' bence sınıf bas mirasına kıyasla oldukça kafa karıştırıcı. Aslında prototiplerle gerçekten miras yoktur, bu yüzden kendi içinde ve yanıltıcı isim, daha çok bir tür 'delegasyon'.

Bunu hayal et ....

Lisedesiniz ve sınıftasınız ve bugün yapılması gereken bir testiniz var, ancak cevaplarınızı dolduracak bir kaleminiz yok. Doh!

Kalemi olabilecek arkadaşın Finnius'un yanında oturuyorsun. Siz soruyorsunuz ve masasına başarısız bir şekilde bakıyor, ama "Kalem yok" demek yerine, diğer arkadaşı Derp ile kalemi olup olmadığını kontrol ettiği hoş bir arkadaş. Derp'in yedek kalemi var ve sınavınızı tamamlamak için size ileten Finnius'a geri veriyor. Derp, kalemi size kullanım için devreten Finnius'a emanet etti.

Burada önemli olan onunla doğrudan bir ilişkiniz olmadığı için Derp'in kalemi size vermemesi .

Bu, aradığınız şey için bir veri ağacının arandığı prototiplerin nasıl çalıştığının basitleştirilmiş bir örneğidir.


3

__proto__ , prototip ve yapıcı ilişkilerini gösteren başka bir şema : resim açıklamasını buraya girin


1

Sadece zaten bir nesneniz var Object.newama yapıcı sözdizimini kullanırken hala bir nesneniz yok.


1

Bir nesnenin prototipi ( Object.getPrototypeOf(obj)kullanımdan kaldırılmış __proto__özellik aracılığıyla veya kullanımdan kaldırılabilir özellik aracılığıyla kullanılabilir ) prototypeile yapıcı işlevlerindeki özellik arasında bir ayrım olduğunu anlamak önemlidir . Birincisi her örnekte özellik, ikincisi de yapıcıdaki özelliktir. Yani, Object.getPrototypeOf(new Foobar())aynı nesneyi ifade eder Foobar.prototype.

Referans: https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Object_prototypes


0

Prototip oluşturur yeni bir nesne mevcut klonlama yoluyla nesneyi . Yani gerçekten prototipi düşündüğümüzde , bir şeyi uydurmak yerine kopyalamayı veya kopyalamayı gerçekten düşünebiliriz .

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.