Kirjautuminen

Haku

Tehtävät

Keskustelu: Ohjelmointikysymykset: JavaScript: Ulkoisesti määritetyt, yksityiset funktiot

Sivun loppuun

Tepi_78 [28.10.2013 15:09:44]

#

Javascriptissä kannattaa määritellä funktiot luokkien ulkopuolelle, jolloin oliot jakavat funktioiden koodin ja muistia säästyy.

En ole löytänyt tapaa toteuttaa private-funktioita ulkoisesti. Onko se edes mahdollista?

groovyb [28.10.2013 15:13:20]

#

Jos esim. käytät KnockoutJS (www.knockoutjs.com), jolla voi toteuttaa mvvm -patternin mukaista ohjelmointia javascriptillä, pysyy ViewModelin funktiot siihen bindatun elementin sisällä. Tavallista javascriptiä tehdessä, en usko että moisia privaattifunktioita pystyy tekemään. tietysti hyvä tapa optimoida javascriptiä on esimerkiksi jakaa funktiot x määrään ulkoisia tiedostoja, jolloin voidaan sivukohtaisesti optimoida mitä js filuja halutaan ladata. On olemassa myös erillisiä laajennoksia ja toteuksia on demand -tyylisesti (esim SharePoint 2010:n client object model toimii tällä periaatteella), jossa haluttu funktio ja tiedosto jossa funktio on, ladataan muistiin vasta kun metodia kutsutaan käyttöön (SP2010:n tapauksessa SP.SOD toteuttaa tämän, esimerkiksi: ExecuteOrDelayUntilScriptLoaded(myCallback, "my_script.js");). Tuon toteutuksesta en tiedä, mutta mahdollista se on.

Metabolix [28.10.2013 15:33:16]

#

JavaScriptin oliomallissa ei ole lainkaan yksityisiä jäseniä. Jos uskot tietäväsi ”oikean” tavan toteuttaa niitä, korjaa käsitystäsi: kyseessä on vain kikka, jolla saadaan tyydyttävä lopputulos. Ei ole mitään velvoitetta käyttää juuri tiettyä kikkaa.

Eräs yleinen tapa (mm. Pythonissa paljon käytetty) on merkitä suojatut ja yksityiset jäsenet alaviivoilla: a._f on suojattu jäsen ja a.__f tai a._luokka__f on yksityinen jäsen. Toteutus vastaa siis täysin tavallisia jäseniä, ja on vain sopimuskysymys, että nämä jäsenet ovat suojattuja.

jukkah [28.10.2013 19:18:02]

#

Kenties yksinkertaisin ja toimivin tapa on käyttää normaalia julkista metodia, ja tarkistaa funkkarin ensimmäisellä rivillä, että this täyttää sille asetetut ehdot.

The Alchemist [28.10.2013 22:46:32]

#

jukkah: tuo ei ratkaise ongelmaa millään tavoin. Ei niitä jäsenmuuttujia voi sen enempää yksityistää kuin jäsenfunktioitakaan. Aivan turhaa kikkailua. Vaikka tuollainen räpeltäminen toimisikin, niin sillä voisi ainoastaan estää kutsumasta jotain funktiota niin että this viittaa eri olioon. Edelleenkin jää mahdollisuus kutsua kyseistä funktiota omistajansa ulkopuolelta, minkä estäminen on ainakin muissa kielissä näkyvyysalueen rajoittamisen idea.

var Foo = {
  iAmNotFake: true,
  _privateMethod: function() {
    if (!this.iAmNotFake) {
      throw new Error('not authorized');
    }
    console.log('call permitted');
  }
};

var Bar = {
  _privateMethod: Foo._privateMethod,
  fooPrivateMethod: function() {
    // magic
    this._privateMethod.call(Foo);
  },
};

// Ei onnistu mutta ihmeen väliä vaikka onnistuisikin?
Bar._privateMethod();

// Onnistuu
Bar.fooPrivateMethod();
Foo._privateMethod();

Tepi_78 [29.10.2013 08:22:11]

#

Metabolix kirjoitti:

JavaScriptin oliomallissa ei ole lainkaan yksityisiä jäseniä. Jos uskot tietäväsi ”oikean” tavan toteuttaa niitä, korjaa käsitystäsi: kyseessä on vain kikka, jolla saadaan tyydyttävä lopputulos. Ei ole mitään velvoitetta käyttää juuri tiettyä kikkaa.

Eräs yleinen tapa (mm. Pythonissa paljon käytetty) on merkitä suojatut ja yksityiset jäsenet alaviivoilla: a._f on suojattu jäsen ja a.__f tai a._luokka__f on yksityinen jäsen. Toteutus vastaa siis täysin tavallisia jäseniä, ja on vain sopimuskysymys, että nämä jäsenet ovat suojattuja.

Onko seuraava tapa vain kikka tehdä yksityinen muuttuja? Ainakaan muuttujaan ei voi viitata olion avulla.

function Luokka()
{
    //Yksityinen muuttuja
    var yksityinenMuuttuja "yksityinen";
    //Julkinen muuttuja
    this.julkinenMuuttuja = "julkinen";

}


.
.
.

var olio = new Luokka();

//Ei toimi
alert(olio.yksityinenMuuttuja);

//Toimii
alert(olio.julkinenMuuttuja);

uta [29.10.2013 09:07:08]

#

Sen voi tehdä paketoimalla koko setti IIFE-funktioon, ja palauttaa returnilla vain ne osat jotka halutaan näkyväksi myös paketin ulkopuolelle. Näin;

var OLIO = (function () {

    var yksityinenMuuttuja = "yksityinen",
        julkinenMuuttuja = "julkinen",
        yksityinenFunktio = function () {

            return 'yksityinen funktio';

        },
        julkinenFunktio = function () {

            return 'julkinen funktio';

        };

    //Toimii sisäpuolella
    //alert(yksityinenMuuttuja);
    //alert(yksityinenFunktio);

    return {
        julkinenMuuttuja: julkinenMuuttuja,
        julkinenFunktio: julkinenFunktio
    };

}());

//Ei toimi ulkopuolella
alert(OLIO.yksityinenMuuttuja);
//alert(OLIO.yksityinenFunktio()); //Heittää typeErrorin

//Toimii
alert(OLIO.julkinenMuuttuja);
alert(OLIO.julkinenFunktio());

Metabolix [29.10.2013 13:12:15]

#

Tepi_78 kirjoitti:

Onko seuraava tapa [var muuttuja] vain kikka tehdä yksityinen muuttuja? Ainakaan muuttujaan ei voi viitata olion avulla.

Tuo on nimenomaan vain kikka. Luokka-funktiossa syntyy vain paikallinen muuttuja, joka häviää heti funktion lopussa. Muuttujalla ei ole mitään tekemistä olion kanssa.

Jos funktion sisällä luodaan lisää funktioita (kuten esimerkissäsi), muuttuja jää elämään funktioiden sisällä ja syntyy vaikutelma ”yksityisestä” muuttujasta. Se on kuitenkin ihan tavallinen muuttuja, samanlainen kuin mikä tahansa var-sanalla esitelty muuttuja. Sillä ei vain enää ole nimeä, joten siihen ei voi myöhemmin viitata.

Tämän takia pyytämäsi ominaisuus ei ole mahdollinen: muuttuja ei liity olioon, vaan se liittyy suoraan niihin funktioihin, jotka luodaan muuttujan näkyvyysalueella. Toisin sanoen jokainen olio tarvitsee omat funktiot, koska ne funktiot ovat joka oliolle yksilölliset: ne sisältävät eri muuttujan.

Jos tuosta syntyisi oikeasti olion yksityinen muuttuja, seuraavan koodin pitäisi toimia:

function Olio() {
	var m = "a"; // Luodaan yksityinen muuttuja..?
}
Olio.prototype.f = function() {
	return this.m; // Viitataan yksityiseen muuttujaan, vai mitä?
};
var o = new Olio();
alert(o.f()); // Tämän pitäisi tulostaa muuttujan arvo "a".
alert(o.m); // Tämän pitäisi antaa virheilmoitus, koska muuttuja on yksityinen.

Koodi tulostaa vain tekstin ”undefined”, eli selvästi mitään yksityistä muuttujaa ei ole olemassakaan.

uta kirjoitti:

Sen voi tehdä paketoimalla koko setti IIFE-funktioon, ja palauttaa returnilla vain ne osat jotka halutaan näkyväksi myös paketin ulkopuolelle.

Tässähän nimenomaan kysyttiin, voisiko tehdä jotenkin sillä tavalla, että funktiot olisivat kaikille olioille yhteisiä. Esimerkissäsi mentiin vain alamäkeen, kun julkisetkin funktiot luotiin erikseen.

Ratkaisu on siis käyttää tavallisia jäseniä, joiden nimet kertovat, että ne ovat suojattuja. Tämä on mielestäni hyvä ratkaisu. Ainoa potentiaalinen varjopuoli on, että tyhmä tai laiska ohjelmoija voi alkaa käyttää yksityisiksi merkittyjä jäseniä ulkopuolelta.

Tepi_78 [29.10.2013 15:21:15]

#

Metabolix kirjoitti:

Tepi_78 kirjoitti:

Onko seuraava tapa [var muuttuja] vain kikka tehdä yksityinen muuttuja? Ainakaan muuttujaan ei voi viitata olion avulla.

Tuo on nimenomaan vain kikka. Luokka-funktiossa syntyy vain paikallinen muuttuja, joka häviää heti funktion lopussa. Muuttujalla ei ole mitään tekemistä olion kanssa.

Loin useita olioita ja tallensin tietorakenteeseen. Var-menetelmällä määritetyt, yksityiset muuttujat olivat edelleen olemassa, kun kyselin niiden arvoja olioilta. Arvot palauttavat funktiot pitää vain määritellä sisäisiksi.

function Luokka(arvo)
{
	var arvo_ = arvo;

	this.asetaArvo = function(arvo)
	{
		arvo_ = arvo;
	}

	this.annaArvo = function()
	{
		return arvo_;
	}
}

Metabolix [29.10.2013 15:22:34]

#

Selitin mielestäni asian aika perusteellisesti. En ole väittänyt, ettei koodi toimisi, vaan olen sanonut, että kyseessä ei ole olion yksityinen jäsen vaan kikka, jolla saadaan samantapainen tulos. Luitko muutakin kuin lainaamasi osan? Kokeilitko ja mietitkö koodiani?

Esimerkiksi tässä käytetään ihan samaa temppua. Miten muuttuja voi olla olion yksityinen jäsen, jos koko koodissa ei esiinny yhtäkään oliota?

var funktio = (function() {
	// Luodaan paikallinen muuttuja.
	var muuttuja = 0;
	// Luodaan nimetön funktio, jonka sisään muuttuja jää elämään.
	return function() {
		return ++muuttuja;
	};
}());

alert([funktio(), funktio(), funktio()]); // 1, 2, 3

Tiesitkö muuten, että JavaScriptissa ei ole myöskään luokkia eikä siten luokan jäsenten määrittelemistä? Koodissasi et ”määrittele funktioita sisäisiksi” vaan luot uusia funktioita ja tallennat ne jonkin olion jäseniksi. Jos olisi luokkia ja niille määriteltyjä funktioita, koko tätä kysymystäsi ei varmaan olisikaan.

jukkah [29.10.2013 16:45:52]

#

The Alchemist kirjoitti:

jukkah: tuo ei ratkaise ongelmaa millään tavoin.

Oho, pääsi unohtumaan, että funktiolla on call-metodi.

Metabolix [30.10.2013 12:43:11]

#

Tässä on Tepi_78:lle vielä toinen yksityisiä muuttujia vastustava esimerkki. Olisi hyödyllistä ymmärtää, mitä tässä tapahtuu ja miksi.

function Olio() {
	var muuttuja;
	this.aseta = function(x) {
		muuttuja = x;
	};
	this.hae = function() {
		return muuttuja;
	};
}

var a = new Olio();
var b = new Olio();
var f = b.aseta;

a.aseta(1);
b.aseta(2);
alert([a.hae(), b.hae()]);
// [1, 2]. Ihan oikein vielä.

// Piti olla sama funktio, eikö?
a.hae = b.hae;
alert([a.hae(), b.hae()]);
// [2, 2]. Mitä?

a.aseta(3);
alert([a.hae(), b.hae()]);
// [2, 2]. Miksi ei muuttunut?

b.aseta(4);
alert([a.hae(), b.hae()]);
// [4, 4]. Nytpä muuttuivat molemmat!

// Sama funktio tämäkin?
b.aseta = a.aseta;
a.aseta(5);
b.aseta(6);
alert([a.hae(), b.hae()]);
// [4, 4]. Mitä ihmettä?!

// Tämä on kai jokin irrallinen funktio?
f(7);
alert([a.hae(), b.hae()]);
// [7, 7]. Magiaa!

Tepi_78 [30.10.2013 15:47:56]

#

Kiitokset neuvoista ja ohjeista!

Javascript on täysin uusi tuttavuus. Minun piti alkaa suunnitella ohjelmistoa, joka toteutetaan javascriptillä. Ensin piti opetella kieltä, jotta tietää, mitä rajoitteita se asettaa suunnittelulle.

Täytyy sanoa, että tässä kohtaa todella kaipaa javaa. Ehkä javascript tarjoaa todella näppäriä mekanismeja, mutta samalla se tarjoaa todella vaarallisia sudenkuoppia ja vaikeasti ymmärrettävää koodia.

Käytän javascriptiä jatkossa kuten perinteistä oliokieltä, esim. javaa. Ajattelen ohjelmistot luokkina ja olioina, vaikka js:ssä ei luokkia olekaan. Yksityiset funktiot ja muuttujat toteutetaan nimeämiskäytännöillä.


Sivun alkuun

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta