Private Members i JavaScript

Tags:    javascript oop private

Hey allesammen!

Jeg sad her forleden og legede lidt med tanken om private members i JavaScript, det var langt fra første gang jeg tænkte på om det var muligt, og det er nok også en af de få ting jeg virkelig ægrer mig over i JavaScript.
I C++ fx, er det bare at skrive private foran din member, og vupti så er den på plads, og kan bruges af de metoder der nu en gang har adgang til denne member. I JavaScript er dette jo nogenlunde muligt, I kender sikkert den constructor-baserede tilgang til private members i en klasse:
Fold kodeboks ind/udJScript kode 


Det er altsammen meget godt, men det ødelægger lidt noget af det allerbedste ved JavaScript, Prototypal-Based OOP. Muligheden for at udvide klasserne med funktioner senere hen, MED adgang til private members er altså her ikke muligt. Man ville være nød til at lave "semi-private" members, ala:
Fold kodeboks ind/udJScript kode 

Det virker altså meget godt, men idéen med private members er lidt død, for de er jo ikke "rigtigt" private.

Jeg ægrede mig i lang tid over dette, indtil jeg for nyligt fandt på en løsningsmodel! Endda en "lovlig" een, uden brug af Function.source, eval eller andre bøller! Funktionen jeg har fundet på har jeg kaldt for Function.Privacy. Normalt er jeg ikke tilhænger af stort begyndelsesbogstav i funktionnavne, men da denne funktion fungere som en konstruktør, syntes jeg at det var passende. Function.Privacy, lyder:
Fold kodeboks ind/udJScript kode 

Først opretter jeg en objekt-variabel, env(ironment), som bruges til at holde de private variabler der defineres/ændres.
Herefter opretter jeg selve objektet (klassen), ved at kalde dens konstruktør, som i dette tilfælde holdes af variablen this. For at kunne kalde konstruktøren med diverse argumenter, benytter jeg mig at et "trick" til at konvertere arguments variablen, til et "ægte" array.
Jeg overfører det det private object der er blevet oprettet i konstruktøren, hvis det ikke er blevet lavet, bliver "private" variablen bar til et tomt object.
Til sidst, gennemløber jeg samtlige funktioner, objektet har, og benytter ECMA 5 funktionen Function.bind, til at "binde" scopet til objektet der returneres af merge metoden, mellem objektets egne public members, og "environments" private members.

Jeg vil yderst gerne høre feedback på denne metode fra jer andre, omkring brugbarhed, forbedringer og diverse kommentarer!
Jeg har allerede performance testet denne metode, og den klare sig selvfølgelig ikke nær så godt som de "normale" OOP metoder, men den er alligevel oppe på ca. 350.000 ops/sec på JSPerf.com
Det er klart at denne metode ikke skal bruges til at oprette en million objekter med, men som en POC eller til mindre projekter med færre kodelinjer, kunne jeg forestille mig denne funktion, som behagelig.

Lad mig høre hvad i tænker!
// Saebekassebil



Nu glemte jeg selvfølgelig et eksempel på brug:
Fold kodeboks ind/udHTML kode 




Det er god research...

Desværre tror jeg at din metode er en lidt for kompleks. Især fordi at det umiddelbart virker forkert at kalde en factory metode, blot for at sikre private access.

Den metode som er generelt anvendt i javascript miljøet ligner meget følgende:
Fold kodeboks ind/udKode 


Alternativ:
Fold kodeboks ind/udKode 


Endnu en:
Fold kodeboks ind/udKode 




Indlæg senest redigeret d. 21.04.2011 15:26 af Bruger #10216
Først og fremmest tak fordi du gad at læse denne smøre.. :P Overvejer faktisk at lave en artikel ud af det med en gennemgang af 'apply', 'call' og 'bind', samt nogle curry metoder, men ved ikke helt hvad stemningen for en sådan artikel er, og om der overhovedet er brug for een.

Men til konktekst: Hvori består forskellen mellem mit første eksempel og dit eksempel? Din konstrutør returnere et object, mens min i en instantieret (hvad hedder det?) funktion. Koster det mindre hukommelse, eller hvad skulle fordelen være, fremfor bare at definere funktionerne i konstruktøren som jeg har gjort?



Har opdateret tidligere indlæg, blot med nye alternativer til nysgerrige folk.

Den store forskel består i at fjerne et dependency. Altså din privacy() function. Nu kender jeg ikke reglerne for C++, men i PHP har du yderligere "access level", som er protected, hvor det derfor også virker ulogisk at have denne mulighed du præsentere.
Af logiske årsager, skal subclasses ikke kunne dele en privat prop. Det skal det egentlig kun være muligt at dele public eller protected props. Men protected props er ikke muligt og derfor indsætter man en underscore for at kunne skelne mellem public og protected.

Gir' det mening?



Jeg er klar over at min Function.Privacy() funktion er et dependency, men det er dog nødvendigt hvis du SAMTIDIGT vil benytte Prototypal-baseret OOP og private members!

Det jeg mente var såmænd ikke forskellen fra min "Privacy" konstruktør til dit eksempel, det er er jo meget tydeligt. Men nærmere forskellen fra mit andet eksempel, hvor jeg definere funktionerne i konstruktøren, som du nu også har lavet et eksempel med.

Jeg har faktisk ikke prøvet at skabe protected members endnu, da JavaScript ikke native har en inherit funktion, men da det er forholdsvist nemt at skrive een, vil jeg lige prøve det af. I'll be back.



Umiddelbart tror jeg faktisk heller ikke din teknik kan prototype efter objektet er oprettet.

I modsætning til teknikken der bare bruger '_' til at illustrerer private:

Fold kodeboks ind/udKode 


Noter splitSecret metoden der bliver protypet på Person funktionen efter john er oprettet, men som John aligevel har.

PS du burde skære dine eksempler ned til at vise det der er relevant og ikke irrelevante ting (kodeordsbeskyttelse), hvis du skriver en artikel om dette.





t