[JavaScript] Prototype Chain

Indította Shyro, 2013 október 31, 01:50:00 DÉLUTÁN

Előző téma - Következő téma

Shyro

Sziasztok!

Mostanaban a webes applikaciofejlesztes keltette fel az erdeklodesemet, egyenlore meg csak kliens oldalon, igy senkit nem lepek meg a jo oreg JavaScript - el. Persze nem a "hozzuk ossze, had villogjon, mukodik ez" vonalat kovetem, hanem a NoAPI/OnlyJS iranyzatot. Erdemes vele komolyabban foglalkozni, hiszen a GitHub public project - jei alapjan a legtobbet hasznalt nyelv (bar ez a tobb ezer API - nak koszonheto) es amugy is a "common" OOP nyelvek mellett kulon kuriozum vele foglalkozni.
Az API - kkal kapcsolatban onnan ered a tartozkodo magatartasom, hogy a tobbseg eros szemfenyvesztes. Pont a JS azon kepessegeit takarjak el (tegyuk hozza ugyenes), amik pont hogy erdekesse teszik a nyelvet. Persze akad olyan API, amit nem szivesen irnek ujra: RequireJS
Masik problemam szemleltetesere - ami a JS felhasznalasaval kapcsolatos - egy idezetet szurnek be:
Idéz... JavaScript ... is ... a highly expressive and flexible prototype-based object-oriented programming language. Once dismissed as a toy for designers to make things such as rollover buttons, today this interesting and unique language is back, stronger than ever. Today's Web 2.0 world of AJAX, fat-client programming, desktop-like rich Internet applications, drag-and-drop maps and webmail clients, rely heavily on JavaScript to provide a highly interactive user experience. And if we never had the chance to properly explore JavaScript before, now is the time to sit down and (re-)learn it.

Sot, tovabb megyek, a JavaScript manapsag meg ennel is tobb. Bar hangsulyozom, azert tul sokat sem kell belelatni.

A neten szamos anyag talalhato, melyek borzasztoan magas hanyada elavult, olykor hibas es eleg morickas megoldasokat kinal. Egy kezemen meg tudom szamolni azon konyvek szamat, amit erdemes forgatni. Netes leirasok kozott is akad sok kiemelkedo, de a reszletekre csak igen kevesen ternek ki.

Ezzel a problemaval szembesultem en is, amikor az oroklodessel foglalkoztam JS - ben. A neten van a problemara megoldas, viszont komolyabb magyarazatot nem igazan fuznek hozza. Mivel akadnaka  forumon a temaban erdekeltek, igy ugy gondoltam megosztom azt, amire ezzel kapcsolatban jutottam.

Inheritance in JS
A JavaScript erosen objektumokra epulo nyelv, annyira, minden minden amit leirunk (a kommenteken kivul) egy objektum. A klasszikus OOP nyelvekben az oroklodest class - ok kozott bonoylitjak le, mig a JS - ben objektumok kozott. Sokak szerint ez es az objektumok rugalmassaga jobban valositja meg az OOP eszmeket, mint mas (en pl. ezt nem feltetlenul mondanam). Ettol fuggetlenul a szabvany jovobeli tervei kozott szerepel a class kulcsszo meghonositasa is.

Objekumok kozotti oroklodes
Minden egyes objektumnak van egy [[Prototype]] property - je, ami szabvany szerint nem olvashato/modosithato. Ez a property tartalmaz egy referenciat az adott objektum szulo-objektumara. Firefox - ban van mod ennek a property - nek az olvasasara: object.__proto__ (sokszor segit)
Amikor letrehozunk egy objektumot (a tomb, a szam, a fuggveny is objektum), akkor automatikusan a [[prototype]] tulajdonsag beallitodik, es az o szulo-objektumara mutat. Pl.:
var object = {};
console.log(object.__proto__);


Constructor
Az objektumoknak van egy masik ([[Prototype]] - hoz hasonlo) rejtett property - je, amely szamunkra szinten erdekes. Ez a property a constructor, amely olvashato/modosithato (az lehetseges, hogy korlatozottan, fene tudja mar) es mindig az adott objektumot letrehozo constructor function - ra mutat. Az elozo pelda eseteben ez az Object().

Constructor function
A constructor function az a fuggveny, amellyel JS - ben kepesek vagyunk osztalyt emulalni. Ez egy eleg eroltett emulalas, de nezzuk meg.
function Class(name) {
    this.name = name;
}
var object = new Class('new obj');

Ebben az esetben:
- object.__proto__ egy Class {} szulo-objektumra hivatkozik
- object.constructor pedig az objektum constructor function - jara (Class(name))

A fuggvenyeknek, igy a constructor function - nak is van prototype property - je, ami nem az objektumok [[Prototype]] tulajdonsaga, hanem egy plusz property. Ez a prototype property adja meg constructor function eseten azt, hogyha egy objektumot hozunk letre a constructor function reven, akkor mi keruljon az objektum [[Prototype]] property - jebe. Ezert ez a prototype property minden function eseteben automatikusan allitodik be, hatha a fuggveny constructor function - kent fog szolgalni.

A mi esetunkben:
function Class() {}
console.log(Class.prototype);

egy Class {} objektumrol beszelunk.

Ezt az objektumot Class.prototype keresztul modosithatjuk is. Pl.:
Class.prototype.name = 'object';

Ha a constructor function reven letrehozunk egy objektumot:
var object = new Class();

Ennek az objektumnak a [[Prototype]] property - je beallitodik a constructor function prototype property - jere (a ket hivatkozas azonos lesz). Erre szoktak felirni ezt a szep egyenloseget:
object.__proto__ === Class.prototype

Mit is jelent ez a gyakorlatban?
Letrehoztunk egy olyan objektumot, amely szulo-objektuma az altalunk name property - vel bovitett objektum.
Azaz, ez mukodni fog:
object.name === 'object'

Ez az egesz szorakozas azt eredmenyezi, hogy kepesek vagyunk JS - ban lancolt oroklodest letrehozni. Nezzuk meg hogyan.

Prototype Chain
Hozzunk letre ket "osztalyt"
function Parent() { this.name = 'object' };
function Child() { this.title = 'child' };


Beallitjuk a Child.prototype - ot egy Parent objektumra:
Child.prototype = new Parent();

Ha mi a Child constructor function reven letrehozunk egy uj objektumot, az mind a Child es mind a Parent constructor function - ban leirt property- kkel rendelkezni fog:
var object = new Child();
object.name === 'object' // true
object.title === 'child' // true


Varjunk, varjunk! Ellenorizzunk! Ha helyes az oroklodes, az object.__proto__ a Child {} szulo-objektumra fog hivatkozni, az object.__proto__.__proto__ pedig a Parent {} szulo-objektumra.
Hoppa! De nem igy tortent, az object.__proto__ is Parent {} szulo-objektum, sot meg az object objektumunk is Parent {}! Igaz, hogy a parameterei megfeleloek, de ha mi ezt a Child constructor function - bol hoztuk letre, akkor legyen is olyan "tipusu" objektum.

A problemat az okozza, hogy amikor az uj objektumunknal a constructor property be akar allitodni, es e reven akarja magat letrehozni akkor elkezdi keresni eloszor a Child() constructor function - ban a constructor propertyt, ami jelen esetben:
Child.constructor === Function()
Ez nem fog tetszeni neki, ezert megnezi a Child.prototype - ban, hogy ott talal e. Az viszont az oroklodesunk miatt:
Parent.constructor === Function()
Szinten ilyesmit ad eredmenyul. Sebaj, megyunk tovabb. Nezzuk meg a Parent.prototype - ban. Voala:
Parent.prototype.constructor === Parent()
Ez mar jo ,ezt mar fel tudja hasznalni az objektum letrehozasahoz, ezt mar be tudja allitani constuctor - nak.
Igy lesz szegeny object objektumunk constructor property - je Parent().

Prototype Chain Fix
Mondhatnank azt, hogy a constructor function - unkban egybol definialjuk a constructort, igy nem fogja mindenfele keresgelni az objektum letrehozasanal:
function Child() {
    this.constructor = Child;
    this.title = 'child';
}

Mukodne is, de nem vart mellekhatasokkal jarna:
object.__proto__ === Parent {}
lenne, ami megint nem jo.

Elarulom a nagy titkot, minden oroklodes eseteben ezzel kell kiegeszitenunk az oroklodest:
Child.prototype.constructor = Child;

Ezzel a modszerrel mint az objektum constructor property - je helyes lesz, az objektum "tipusa" is, es az oroklodesi lanc is helyreall. (object.__proto__ === Child {} && object.__proto__.__proto__ === Parent {})

Osszessegeben a kod:

function Parent() {
    this.name = 'object';
}

function Child() {
    this.title = 'child';
}

Child.prototype = new Parent();
Child.prototype.constructor = Child;
var object = new Child();


Ezzel meg korantsem magyaraztam el mindent a JS prototype alapu oroklodeserol, de remelhetoleg igy mar erthetove valik a Prototype Chain Fix.  Sot, ha elenk tolnak egy design patternt legalabb mar ertjuk, miert is mukodik. :)
makeSystem :: Integral a => [a] -> [a]
makeSystem l = concat (zipWith (\ a b -> replicate (fromIntegral a) (fromIntegral b)) l [ product x | x <- inits l ])
makeSystem [ 60, 60, 24, 7, 52 ] = ?

Powered by EzPortal