Javascript'teki fonksiyonların gizemi çözülemiyor


16

Javascript'in perde sahnelerini anlamaya çalışıyorum ve nesnelerin, özellikle Nesne ve Fonksiyonun yaratılmasını ve aralarındaki ilişkiyi anlamaya sıkıştım .

Array, String vb.Gibi tüm yerleşik nesnelerin Object'den uzantı (devralınan) olduğunu okuduğumda, Object'in yaratılan ilk nesne olduğunu ve kalan nesnelerin kendisinden miras aldığını varsaydım. Ancak, Nesnelerin yalnızca işlevler tarafından oluşturulabileceğini bilmeniz mantıklı değildir, ancak işlevler de İşlev nesnelerinden başka bir şey değildir. Kulağa tavuk ve tavuk ikilemi gibi gelmeye başladı.

Diğer son derece kafa karıştırıcı olan şey, eğer console.log(Function.prototype)bir işlev yazdırırsam, ancak console.log(Object.prototype)yazdırdığımda bir nesne yazdırır. Neden Function.prototypebir nesne olması gerektiği bir fonksiyon?

Ayrıca, Mozilla belgelerine göre, her javascript nesnenin functionuzantısıdır, Functionancak siz console.log(Function.prototype.constructor)tekrar bir işlevdir. Şimdi bir şeyi kendiniz yaratmak için nasıl kullanabilirsiniz (Mind = blown).

Son şey, Function.prototypebir işlevdir, ancak constructorişlevi kullanarak bu nesneye dönen bir işlev Function.prototype.constructoranlamına gelir erişebilirsinizFunction.prototypeprototype


bir işlev bir nesne olduğu için Function.prototype, bunun bir işlev olabileceği ve iç alanlara sahip olabileceği anlamına gelir . Yani hayır, prototip işlevini yapısından geçerken yürütmezsiniz. Son olarak, Javascript'i yorumlayan bir motor olduğunu unutmayın, bu nedenle Nesne ve İşlev, Javascript'ten değil, motor içinde oluşturulabilir ve özel referans gibi Function.prototypeve Object.prototypesadece motor tarafından özel bir şekilde yorumlanabilir.
Walfrat

1
Standartlara uygun bir JavaScript derleyicisi uygulamak istemiyorsanız, gerçekten bu şeyler için endişelenmenize gerek yoktur. Yararlı bir şey yapmak istiyorsanız, ders dışısınız demektir.
Jared Smith

5
FYI "tavuk ve tavuk ikilemi" için olağan İngilizce ifade "tavuk ve yumurta sorunu", yani "ilk gelen tavuk, yumurta mı?" (Elbette cevap yumurtadır. Yumurtlayan hayvanlar tavuklardan milyonlarca yıl önce vardı.)
Eric Lippert

Yanıtlar:


32

Javascript'in perde sahnelerini anlamaya çalışıyorum ve nesnelerin, özellikle Nesne ve Fonksiyonun yaratılmasını ve aralarındaki ilişkiyi anlamaya sıkıştım.

Bu karmaşık, yanlış anlaşılması kolaydır ve birçok yeni başlayan Javascript kitabı yanlış anlıyor, bu yüzden okuduğunuz her şeye güvenmeyin.

1990'larda ve standardizasyon komitesinde Microsoft'un JS motorunun uygulayıcılarından biriydim ve bu yanıtı bir araya getirirken bir takım hatalar yaptım. (15 yılı aşkın süredir bu konuda çalışmadığım için affedilebilirim.) Bu zor şeyler. Ama prototip mirasını anladıktan sonra, her şey mantıklı.

Array, String vb.Gibi tüm yerleşik nesnelerin Object'den uzantı (devralınan) olduğunu okuduğumda, Object'in yaratılan ilk nesne olduğunu ve kalan nesnelerin kendisinden miras aldığını varsaydım.

Derse dayalı kalıtım hakkında bildiğiniz her şeyi atın. JS, prototip tabanlı miras kullanır.

Ardından, kafanızda "kalıtım" ın ne anlama geldiğini çok açık bir şekilde tanımladığınızdan emin olun. C # veya Java veya C ++ gibi OO dillerine alışkın olan insanlar, kalıtımın alt tipleme olduğunu düşünürler, ancak miras alt tipleme anlamına gelmez. Kalıtım, bir şeyin üyelerinin de başka bir şeyin üyeleri olduğu anlamına gelir . O değil mutlaka bu şeyler arasında bir alt-tiplemesi ilişki olduğunu demek! Tür teorisindeki pek çok yanlış anlama, insanların bir fark olduğunu fark etmemelerinin sonucudur.

Ancak, Nesnelerin yalnızca işlevler tarafından oluşturulabileceğini bilmeniz mantıklı değildir, ancak işlevler de İşlev nesnelerinden başka bir şey değildir.

Bu sadece yanlış. Bazı nesneler vardır değil arayarak yarattığı new Fbazı fonksiyon için F. Bazı nesneler JS çalışma zamanı tarafından hiçbir şey olmadan oluşturulur. Herhangi bir tavuk tarafından bırakılmayan yumurtalar var . Bunlar, başlangıçta çalışma zamanı tarafından oluşturuldu.

Kuralların ne olduğunu söyleyelim ve belki de bu yardımcı olacaktır.

  • Her nesne örneğinin bir prototip nesnesi vardır.
  • Bazı durumlarda bu prototip olabilir null.
  • Nesne örneğindeki bir üyeye erişirseniz ve nesnede bu üye yoksa, nesne prototipini savunur veya prototip boşsa durur.
  • prototypeBir nesnenin üyesi genellikle değil nesnenin prototip.
  • Daha ziyade, prototypebir fonksiyon nesnesinin F üyesi, tarafından yaratılan nesnenin prototipi haline gelecek olan nesnedir new F().
  • Bazı uygulamalarda, örnekler __proto__prototiplerini gerçekten veren bir üye alır. (Bu artık kullanımdan kaldırılmıştır. Buna güvenmeyin.)
  • İşlev nesneleri prototype, oluşturulduklarında atanan yepyeni bir varsayılan nesneye sahip olur.
  • Bir fonksiyon nesnesinin prototipi elbette Function.prototype.

Özetleyelim.

  • Prototip ObjectDİRFunction.prototype
  • Object.prototype nesne prototip nesnesidir.
  • Prototip Object.prototypeDİRnull
  • Prototip FunctionDİR Function.prototype- bu nadir durumlardan biridir Function.prototypeaslında bir prototipi Function!
  • Function.prototype işlev prototip nesnesidir.
  • Prototip Function.prototypeDİRObject.prototype

Farzedelim ki bir işlev Foo yapıyoruz.

  • Prototip FooDİR Function.prototype.
  • Foo.prototype Foo prototip nesnesidir.
  • Prototip Foo.prototypeDİR Object.prototype.

Varsayalım diyelim new Foo()

  • Yeni nesnenin prototipi Foo.prototype

Bunun mantıklı olduğundan emin olun. Hadi çizelim. Ovaller nesne örnekleridir. Kenarlar ya __proto__"prototipi" prototypeanlamına gelir ya da " prototypeözelliği " anlamına gelir .

resim açıklamasını buraya girin

Çalışma zamanının tek yapması gereken, tüm bu nesneleri oluşturmak ve çeşitli özelliklerini buna göre atamaktır. Eminim bunun nasıl yapılacağını görebilirsiniz.

Şimdi bilginizi test eden bir örneğe bakalım.

function Car(){ }
var honda = new Car();
print(honda instanceof Car);
print(honda.constructor == Car);

Bu ne yazdırıyor?

Ne instanceofdemek istiyorsun? honda instanceof Car" prototip zincirindeki Car.prototypeherhangi bir nesneye eşit hondami?"

Evet öyle. honda'ın prototipi Car.prototype, yani işimiz bitti. Bu doğru yazdırır.

İkincisi ne olacak?

honda.constructormevcut olmadığından prototipe danışıyoruz, yani Car.prototype. Ne zaman Car.prototypenesne otomatik bir özellik verildi oluşturulduğu constructoriçin eşit Carbu doğrudur, bu yüzden.

Şimdi buna ne dersiniz?

var Animal = new Object();
function Reptile(){ }
Reptile.prototype = Animal;
var lizard = new Reptile();
print(lizard instanceof Reptile);
print(lizard.constructor == Reptile);

Bu program ne yazdırıyor?

Yine, lizard instanceof Reptile" prototip zincirindeki Reptile.prototypeherhangi bir nesneye eşit lizardmi?"

Evet öyle. lizard'ın prototipi Reptile.prototype, yani işimiz bitti. Bu doğru yazdırır.

Şimdi ne olacak

print(lizard.constructor == Reptile);

Bunun da lizardinşa edildiğinden beri doğru yazdırdığını düşünebilirsiniz, new Reptileancak yanılıyorsunuz. Nedeni.

  • Mu lizardbir var constructormülkiyet? Hayır. Bu yüzden prototipe bakıyoruz.
  • Prototipi lizardolduğu Reptile.prototypeolan Animal.
  • Mu Animalbir var constructormülkiyet? Hayır. Bu yüzden prototipine bakıyoruz.
  • Prototipi Animalolan Object.prototypeve Object.prototype.constructorçalışma zamanı tarafından oluşturulan ve eşit olduğunu Object.
  • Bu yanlış yazdırıyor.

Reptile.prototype.constructor = Reptile;Orada bir noktada söylemeliydik , ama hatırlamıyorduk!

Her şeyin sizin için anlamlı olduğundan emin olun. Hala kafa karıştırıcıysa bazı kutular ve oklar çizin.

Diğer son derece kafa karıştırıcı olan şey, eğer console.log(Function.prototype)bir işlev yazdırırsam, ancak console.log(Object.prototype)yazdırdığımda bir nesne yazdırır. Neden Function.prototypebir nesne olması gerektiği bir fonksiyon?

İşlev prototipi çağrıldığında geri dönen bir işlev olarak tanımlanır undefined. Bunu zaten biliyoruz Function.prototypeolduğu Functionişin garibi, prototip. Bu nedenle Function.prototype()yasaldır ve bunu yaptığınızda undefinedgeri dönersiniz. Yani bu bir işlev.

ObjectPrototip bu özelliği yoktur; çağrılabilir değil. Bu sadece bir nesne.

console.log(Function.prototype.constructor)o zaman tekrar bir işlevdir.

Function.prototype.constructoradildir Functionaçıkçası. Ve Functionbir işlevdir.

Şimdi bir şeyi kendiniz yaratmak için nasıl kullanabilirsiniz (Mind = blown).

Bunu fazla düşünüyorsun . Gereken tek şey, çalışma zamanının başladığında bir grup nesne oluşturmasıdır. Nesneler, dizeleri nesnelerle ilişkilendiren arama tablolarıdır. Çalışma zamanı başladığında, o yapması gereken atama başlatmak sonra bir kaç düzine boş nesneler oluşturmak ve olan prototype, __proto__, constructorve bu yüzden yaptıkları gerektiğini grafiği yapana kadar her nesnenin özelliklerine.

Size yukarıda verdiğim şemayı alıp constructorona kenarlar eklemeniz yararlı olacaktır. Bunun çok basit bir nesne grafiği olduğunu ve çalışma zamanının onu oluştururken sorun yaşamayacağını hızlı bir şekilde göreceksiniz.

İyi bir egzersiz kendiniz yapmak olacaktır. İşte, ben başlayacağım. Biz kullanacağız my__proto__anlamına "prototipi nesnesi" ve myprototype"prototipi özelliğini" demek.

var myobjectprototype = new Object();
var myfunctionprototype = new Object();
myfunctionprototype.my__proto__ = myobjectprototype;
var myobject = new Object();
myobject.myprototype = myobjectprototype;

Ve bunun gibi. Programın geri kalanını, "gerçek" Javascript yerleşik nesneleriyle aynı topolojiye sahip bir nesne kümesi oluşturmak için doldurabilir misiniz? Bunu yaparsanız, son derece kolay olduğunu göreceksiniz.

JavaScript'teki nesneler, yalnızca dizeleri diğer nesnelerle ilişkilendiren arama tablolarıdır . Bu kadar! Burada sihir yok. Kendinizi düğümlere bağlıyorsunuz, çünkü her nesnenin bir kurucu tarafından yaratılması gerektiği gibi, aslında var olmayan kısıtlamaları hayal ediyorsunuz.

İşlevler yalnızca ek bir özelliğe sahip nesnelerdir: çağrılacak. Bu yüzden küçük simülasyon programınızı gözden geçirin ve .mycallableher nesneye çağrılabilir olup olmadığını gösteren bir özellik ekleyin . Bu kadar basit.


9
Son olarak, kısa, özlü, anlaşılması kolay bir JavaScript açıklaması! Mükemmel! Birimiz muhtemelen nasıl karıştırılabilirdi? :) Her ne kadar tüm ciddiyette, arama tabloları olmak nesneler hakkında son bit gerçekten anahtarıdır. Deliliğe bir yöntem var --- ama yine de delilik ...
Greg Burghardt

4
@GregBurghardt: İlk başta karmaşık göründüğüne katılıyorum, ancak karmaşıklık basit kuralların sonucudur. Her nesnenin bir __proto__. __proto__Nesne prototipi boş. __proto__Arasında new X()olduğunu X.prototype. Tüm işlev nesneleri __proto__, işlev prototipinin kendisi dışında işlev prototipine sahiptir. Objectve Functionişlev prototipi işlevlerdir. Bu kuralların hepsi basittir ve ilk nesnelerin grafiğinin topolojisini belirlerler.
Eric Lippert

6

Zaten çok sayıda mükemmel cevabınız var, ancak tüm bunların nasıl çalıştığı ile ilgili cevabınıza kısa ve net bir cevap vermek istiyorum ve bu cevap:

SİHİRLİ !!!

Gerçekten, hepsi bu.

ECMAScript yürütme motorları uygulamak insanlar zorunda uygulamak ECMAscript kurallarına değil uymak onlar tarafından dahilinde onların uygulanması.

ECMAScript Spesifikasyonu A'nın B'den miras aldığını, ancak B'nin A? Sorun değil! Bir prototip işaretçisi ile bir ilk NULLoluşturun, B'yi A örneği olarak oluşturun ve daha sonra A'nın prototip işaretçisini B'yi işaret ederek daha sonra B'ye getirin. Çantada keklik.

Diyorsun ama bekle, ECMAScript'te prototip işaretçisini değiştirmenin bir yolu yok! Ama şöyle bir şey var: Bu kod çalışmadığı üzerinde ECMAScript motoru bu kod olan ECMAScript motoru. Bu mu motorda ECMAScript kod çalışan yok nesnelerin iç yapısına erişim hakkına sahiptir. Kısacası: ne isterse yapabilir.

Bu arada, gerçekten yapmak istiyorsanız, bunu sadece bir kez yapmanız gerekir: daha sonra, örneğin dahili belleğinizi dökebilir ve ECMAScript motorunuzu her başlattığınızda bu dökümü yükleyebilirsiniz.

ECMAScript motorunun kendisi ECMAScript'te yazılmış olsa bile, bunların hepsinin hala geçerli olduğunu unutmayın (örneğin Mozilla Narcissus için olduğu gibi). O zaman bile , motoru uygulayan ECMAScript kodu , elbette üzerinde çalıştığı motora erişimi olmamasına rağmen , uyguladığı motora tam erişime sahiptir .


3

ECMA spec 1'den

ECMAScript, C ++, Smalltalk veya Java gibi uygun sınıfları içermez, bunun yerine, nesneler için depolama alanı ayıran ve özelliklerine başlangıç ​​değerleri atayarak bunların tümünü veya bir kısmını başlatan kod yürüterek nesne oluşturan yapıcıları destekler. Yapıcılar dahil tüm işlevler nesnedir, ancak tüm nesneler yapıcı değildir.

Nasıl daha net olabileceğini görmüyorum !!! </sarcasm>

Daha aşağıda görüyoruz:

Prototip Prototip, ECMAScript'te yapı, durum ve davranış mirasını uygulamak için kullanılan bir nesnedir. Bir kurucu bir nesne oluşturduğunda, o nesne, özellik referanslarını çözümlemek amacıyla kurucunun ilişkili prototipine dolaylı olarak başvurur. Yapıcı ile ilişkili prototip, program ifadesi 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.

Dolayısıyla, bir prototipin bir nesne olduğunu, ancak mutlaka bir işlev nesnesi olmadığını görebiliriz.

Ayrıca, bu ilginç titbit var

http://www.ecma-international.org/ecma-262/8.0/index.html#sec-object-objects

Object yapıcısı,% Object% intrinsic nesnesidir ve global nesnenin Object özelliğinin başlangıç ​​değeridir.

ve

İşlev yapıcısı,% Function% intrinsic nesnesidir ve global nesnenin Function özelliğinin başlangıç ​​değeridir.


Şimdi öyle. ECMA6 sınıflar oluşturmanıza ve bunlardan nesneleri başlatmanıza olanak tanır.
ncmathsadist

2
@ncmathsadist ES6 sınıfları sadece sözdizimsel bir şekerdir, anlambilim aynıdır.
Hamza Fatmi

1
Sizin sarcasmlakap aksi halde bu metin gerçekten bir acemi oldukça opaktır.
Robert Harvey

doğru, daha sonra daha eklemek, bazı kazma yapmak gerekir
Ewan

1
eee?
Ewan

1

Aşağıdaki türler JavaScript'teki her değeri kapsar:

  • boolean
  • number
  • undefined(tek değeri içerir undefined)
  • string
  • symbol (referans ile karşılaştırılan soyut benzersiz "şeyler")
  • object

JavaScript'teki her nesnenin (yani her şeyin) bir tür nesnesi olan bir prototipi vardır.

Prototip aynı zamanda bir tür nesne 1 olan fonksiyonlar içerir .

Nesneler aynı zamanda bir işlev olan bir kurucuya ve dolayısıyla bir tür nesneye sahiptir.

İç içe

Her şey özyinelemelidir, ancak JavaScript kodunun aksine, JavaScript işlevlerini çağırmak zorunda kalmadan nesneler oluşturabileceğinden (nesneler yalnızca uygulamanın denetlediği bellek olduğundan) uygulama otomatik olarak yapabilir.

Dinamik olarak yazılan birçok dilde çoğu nesne sistemi böyle dairesel 2'dir . Örneğin, Python'da sınıflar nesnelerdir ve sınıflar sınıfı typebu typeyüzden kendisinin bir örneğidir.

En iyi fikir, sadece dilin sağladığı araçları kullanmak ve oraya nasıl geldiklerini çok düşünmemek.

1 İşlevler oldukça özeldir çünkü çağırılabilirler ve opak veriler (gövdeleri ve muhtemelen bir kapatma) içerebilen tek değerlerdir.

2 Aslında, kendi üzerinde geriye doğru eğilmiş işkence, dallanmış bir şerit, ama "dairesel" yeterince yakın.

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.