Şu 2 örneği ele alalım:
var A = function() { this.hey = function() { alert('from A') } };
vs.
var A = function() {}
A.prototype.hey = function() { alert('from prototype') };
Buradaki çoğu insan (özellikle en yüksek puanlı cevaplar) NEDEN'i açıklamadan nasıl farklı olduklarını açıklamaya çalıştı. Bunun yanlış olduğunu düşünüyorum ve önce temel ilkeleri anlarsanız, fark belirginleşecektir. İlk önce temel ilkeleri açıklamaya çalışalım ...
a) İşlev, JavaScript'teki bir nesnedir. JavaScript'teki HER nesne dahili bir özellik alır (yani, Chrome gibi tarayıcılar dışında diğer özellikler gibi erişemezsiniz), genellikle olarak adlandırılır __proto__
( anyObject.__proto__
referansta bulunduğunu görmek için gerçekten Chrome'a yazabilirsiniz . JavaScript'te bir özellik = bir nesnenin içindeki bir değişken, başka bir şey yok. Değişkenler ne yapar?
Peki bu __proto__
özellik neyi gösteriyor? Genellikle başka bir nesne (nedenini daha sonra açıklayacağız). Özelliğin JavaScript'i __proto__
başka bir nesneyi DEĞİL olarak göstermesini zorlamanın tek yolu kullanmaktır var newObj = Object.create(null)
. Bunu __proto__
yapsanız bile, STILL özelliği nesnenin bir özelliği olarak var olur, sadece başka bir nesneyi göstermez, işaret eder null
.
İşte çoğu insanın kafası karışıyor:
JavaScript'te yeni bir işlev oluşturduğunuzda (bu da bir nesne, unutmayın?), Tanımlandığı anda, JavaScript otomatik olarak adlandırılan bu işlevde yeni bir özellik oluşturur prototype
. Dene:
var A = [];
A.prototype // undefined
A = function() {}
A.prototype // {} // got created when function() {} was defined
A.prototype
tesisten TAMAMEN FARKLI __proto__
. Örneğimizde, 'A' artık 'prototip' ve olarak adlandırılan İKİ özelliğe sahiptir __proto__
. Bu insanlar için büyük bir karışıklık. prototype
ve __proto__
özellikler hiçbir şekilde ilişkili değildir, ayrı değerlere işaret eden ayrı şeylerdir.
Merak edebilirsiniz: JavaScript'in neden __proto__
her bir nesnede özelliği var? Bir kelime: delegasyon . Bir nesnedeki bir özelliği çağırdığınızda ve nesnenin sahip olmadığı, JavaScript tarafından başvurulan nesneyi __proto__
olup olmadığını görmek için onu arar . Eğer sahip değilse, o nesnenin __proto__
özelliğine bakar ve böylece zincir bitene kadar devam eder. Böylece prototip zincirinin adı . Tabii ki, __proto__
bir nesneye işaret etmez ve bunun yerine null
, iyi şanslar gösterirse, JavaScript bunu fark eder ve undefined
mülk için size geri döner .
Ayrıca, prototype
işlevi tanımladığınızda neden JavaScript bir işlev için çağrılan bir özellik oluşturduğunu merak ediyor olabilirsiniz. Sizi kandırmaya çalışıyor Çünkü evet sizi aldatmasına o sınıf tabanlı diller gibi çalıştığını.
Örneğimizle devam edelim ve aşağıdakilerden bir "nesne" oluşturalım A
:
var a1 = new A();
Bu şey olduğunda arka planda bir şey oluyor. a1
yeni, boş bir nesne atanmış sıradan bir değişkendir.
new
İşlevi bir işlev çağırma işleminden önce kullanmış olmanız A()
arka planda ek bir şey yaptı. new
Anahtar kelime hangi şimdi başvuruları yeni bir nesne oluşturulur a1
ve bu nesne boştur. Ek olarak olanlar:
Her fonksiyon tanımında, yaratılan yeni bir özellik prototype
( __proto__
özellikten farklı olarak erişebilirsiniz ) oluşturulduğunu söyledik ? Bu özellik şu anda kullanılıyor.
Şimdi, yeni pişirilmiş boş bir a1
nesneye sahip olduğumuz noktadayız . JavaScript'teki tüm nesnelerin, boş veya başka bir nesne olsun, bir __proto__
şeye ( a1
ayrıca sahip) işaret eden bir dahili özelliğe sahip olduğunu söyledik. Ne new
operatör yapar o setleri olmasıdır __proto__
işlevin işaret edecek özelliğini prototype
özelliği. Tekrar okuyun. Temel olarak bu:
a1.__proto__ = A.prototype;
Bunun A.prototype
boş bir nesneden başka bir şey olmadığını söyledik (tanımlamadan önce başka bir şeye değiştirmediğimiz sürece a1
). Şimdi temelde a1.__proto__
aynı şeye A.prototype
işaret ediyor, yani bu boş nesne. Her ikisi de, bu çizgi gerçekleştiğinde oluşturulan aynı nesneyi işaret ediyor:
A = function() {} // JS: cool. let's also create A.prototype pointing to empty {}
Şimdi, var a1 = new A()
ifade işlendiğinde başka bir şey daha oluyor . Temel A()
olarak yürütülür ve A böyle bir şeyse:
var A = function() { this.hey = function() { alert('from A') } };
İçindeki tüm şeyler function() { }
idam edilecek. Çizgiye ulaştığınızda this.hey..
, this
olarak değiştirilir a1
ve bunu alırsınız:
a1.hey = function() { alert('from A') }
Neden this
değişiklik yapıldığını açıklamayacağım , a1
ancak bu daha fazla bilgi edinmek için harika bir cevap .
Özetlemek gerekirse, yaptığınız zaman var a1 = new A()
arka planda 3 şey oluyor:
- Tamamen yeni bir boş nesne oluşturulur ve bu nesneye atanır
a1
.a1 = {}
a1.__proto__
özelliği, A.prototype
noktalarla aynı şeyi işaret edecek şekilde atanır (başka bir boş nesne {})
İşlev A()
, this
1. adımda oluşturulan yeni, boş nesneye ayarlanarak yürütülür (neden this
değişiklik yaptığına dair yukarıda atıfta bulunduğum yanıtı okuyun a1
)
Şimdi başka bir nesne oluşturmaya çalışalım:
var a2 = new A();
Adım 1,2, 3 tekrar edilecektir. Bir şey fark ettin mi? Anahtar kelime tekrar. Adım 1: a2
yeni bir boş nesne olacak, adım 2: __proto__
özelliği aynı şeyi A.prototype
işaret edecek ve en önemlisi adım 3: işlev A()
AGAIN yürütülür, yani bir işlev içeren özellik a2
elde edilir hey
. a1
ve a2
adında iki AYRI özelliklere sahip hey
2 AYRI fonksiyonlara işaret! Şimdi aynı iki farklı nesnede aynı şeyi yapan yinelenen işlevler var, ayy ... 1000 nesne varsa new A
, tüm işlev bildirimleri 2 sayısı gibi bir şeyden daha fazla bellek aldıktan sonra bunun bellek sonuçlarını hayal edebilirsiniz . bunu nasıl önleyebiliriz?
__proto__
Özelliğin neden her nesnede var olduğunu hatırlıyor musunuz? Böylece, yoMan
özelliği a1
( varsa) alırsanız, mülküne __proto__
danışılır, ki bu bir nesne ise (ve çoğu durumda), içerip içermediğini kontrol eder yoMan
ve içermiyorsa, nesnenin __proto__
vb. bilgilerine başvurur . Varsa, o özellik değerini alır ve size görüntüler.
Birisi bu gerçeği + oluşturduğunuzda a1
, __proto__
mülkünün aynı (boş) nesneyi A.prototype
işaret ettiği ve bunu işaret ettiği gerçeğini kullanmaya karar verdi :
var A = function() {}
A.prototype.hey = function() { alert('from prototype') };
Güzel! Şimdi, oluşturduğunuzda a1
, yine yukarıdaki 3 adımın hepsinden geçer ve 3. adımda function A()
, hiçbir şey yapmaz, çünkü yürütülecek hiçbir şey yoktur. Ve eğer yaparsak:
a1.hey
İçermediğini görecek ve a1
sahip olup olmadığını görmek hey
için __proto__
özellik nesnesini kontrol edecektir .
Bu yaklaşımla, her yeni nesne oluşturmada işlevlerin çoğaltıldığı 3. adımdaki bölümü ortadan kaldırıyoruz. Ayrı bir mülk yerine a1
ve a2
sahip olmak yerine hey
, şimdi HİÇBİRİ ona sahip değildir. Hangi, sanırım, şimdiye kadar kendini anladın. Bu iyi bir şey ... anlarsanız __proto__
ve bunun Function.prototype
gibi sorular oldukça açık olacaktır.
NOT: Bazı insanlar iç Prototip özelliğini olarak adlandırmama eğilimindedir __proto__
, bu adı posta yoluyla Functional.prototype
özelliği iki farklı şey olarak açıkça ayırt etmek için kullandım .