Kurssin tavoitteena on ollut JavaScriptin sisäisen toiminnan ymmärtäminen, kielen "pelimoottorin" logiikan hahmottaminen. JavaScript-ohjelman suoritusaikainen dynaamisuus – kaikkea voi vauhdissa muuttaa – on johtanut siihen, että on mahdollista kehittää hyvin erilaisia ohjelmointityylejä ja -tapoja. Ja onhan niitä kehiteltykin!
JavaScript-ohjelman suoritus kuitenkin aina lopulta palautuu dynaamisesti muokattavaan assosiaatiolistojen verkkoon. Erilaisten ohjelmointityylien ja -tekniikoiden ymmärtämisen edellytys on tuon verkon rakenteen ymmärtäminen. Siihen perustuen on mahdoillista kehitellä myös kokonaan uusia tyylejä ja ehkäpä vieläkin parempia rakenneratkaisuja.
Tässä luvussa kurkistellaan esimerkinomaisesti joihinkin verkosta löytyviin JavaScript-ohjelmoinnin rakennehahmoihin suunnittelumalleihin ("design pattern").
function Otus(nimi) { // Tämä on se tuttu ja tapa! this.nimi=nimi this.laji="kissa" } Otus.prototype.kuka= function() {write(this.nimi+" "+this.laji)} var x = new Otus("Missu") x.kuka() // Missu kissa var y = new Otus("Murre") y.kuka() // Murre kissa y.laji = "koira" y.kuka() // Murre koira y.kuka = function() {write("Hau, hau!")} y.kuka() // Hau, hau! x.kuka() // Missu kissa
function Otus(nimi) { this.nimi=nimi this.laji="kissa" // yksityinen kenttä ja yksityinen funktio: var lkm=3 function viela() {lkm -= 1; return lkm>=0} // talletetaan luontihetken this yksityiseen kenttään that: var that = this // jukinen funktio: this.kuka = function() {if (viela()) write(that.nimi+" "+that.laji) else write("Kaikki "+that.nimi+"-kysymykset käytetty!") } } var x = new Otus("Missu") var y = new Otus("Murre") y.laji = "koira" x.kuka() // Missu kissa x.kuka() // Missu kissa x.kuka() // Missu kissa y.kuka() // Murre koira x.kuka() // Kaikki Missu-kysymykset käytetty! y.kuka() // Murre koira x.kuka() // Kaikki Missu-kysymykset käytetty! y.kuka() // Murre koira y.kuka() // Kaikki Murre-kysymykset käytetty!
function Olio(nimi) { this.nimi=nimi } Olio.prototype.postilaatikko=0 Olio.prototype.postita=function(a) {Olio.prototype.postilaatikko=a} var x = new Olio("abc") var y = new Olio("def") x.postita("Moi! Mitä kuuluu?") write(y.postilaatikko) // Moi! Mitä kuuluu? y.postita("Kiitos hyvää.") write(x.postilaatikko) // Kiitos hyvää.
x.postilaatikko = "EVVK!" write(y.postilaatikko) // Kiitos hyvää.
var laskuri = (function () { var counter = 0; // sulkeuman yksityinen muuttuja // funktio palauttaa arvonaan olion, jossa on kaksi aksessoria, // jotka käsittelevät sulkeumaan suljettua vapaata muuttujaa: return { incrementCounter: function () { return counter++; }, resetCounter: function () { write( "Laskurin arvo ennen nollausta: " + counter ); counter = 0; } }; })(); // HUOM: Funktio siis suoritetaan saman tien! write(laskuri.incrementCounter()) // 0 write(laskuri.incrementCounter()) // 1 write(laskuri.incrementCounter()) // 2 laskuri.resetCounter(); // Laskurin arvo ennen nollausta: 3 write(laskuri.incrementCounter()) // 0 write(laskuri.incrementCounter()) // 1
laskuri.incrementCounter = () => 666 write(laskuri.incrementCounter()) // 666 write(laskuri.incrementCounter()) // 666 write(laskuri.incrementCounter()) // 666 ...
function Olio(nimi) { this.nimi=nimi } Olio.prototype.posti = (function () { var postilaatikko=0 return { laheta: function(a) {postilaatikko=a}, lue: function() {return postilaatikko} } })() var x = new Olio("abc") var y = new Olio("def") x.posti.laheta("Moi! Mitä kuuluu?") write(y.posti.lue()) // Moi! Mitä kuuluu? y.posti.laheta("Kiitos hyvää.") write(x.posti.lue()) // Kiitos hyvää. x.posti.laheta("EVVK!") write(y.posti.lue()) // EVVK! y.posti.laheta(123) write(x.posti.lue()) // 123
function teeUusiLaskuri() { var counter = 0; // sulkeuman yksityinen muuttuja // funktio palauttaa arvonaan olion, jossa on kaksi funktiota, // jotka käsittelevät sulkeuman yhteistä muuttujaa: return { incrementCounter: function () { return counter++; }, resetCounter: function () { write( "Laskurin arvo ennen nollausta: " + counter ); counter = 0; } }; } // HUOM: Funktiota ei suoriteta vielä! var x = teeUusiLaskuri() write(x.incrementCounter()) // 0 write(x.incrementCounter()) // 1 write(x.incrementCounter()) // 2 x.resetCounter(); // Laskurin arvo ennen nollausta: 3 var y = teeUusiLaskuri() write(y.incrementCounter()) // 0 write(y.incrementCounter()) // 1 write(y.incrementCounter()) // 2 write(y.incrementCounter()) // 3 write(y.incrementCounter()) // 4 y.resetCounter(); // Laskurin arvo ennen nollausta: 5 write(x.incrementCounter()) // 0 write(x.incrementCounter()) // 1
(function () { // - muuttujien ja funktioiden määrittelyitä // - täältä näkee ulos, mutta ulkoa ei näy tänne // - tänne voidaan sulkea ympäristön vapaita muuttujia }());
var MODULE = (function () { // privaattiosuus: var privaatti = 1 function privaattimetodi() { // ... } var my = {} // tähän rakennetaan julkinen rajapinta // julkinen rajapinta: my.julkinen = 456; my.privaatinArvo = function() {return privaatti} my.kasvataPrivaattia = function (x) {privaatti += x} return my; }()); write(MODULE.julkinen) // 456 write(MODULE.privaatinArvo()) // 1 MODULE.kasvataPrivaattia(22) write(MODULE.privaatinArvo()) // 23
Sivu Brainfuck beware: JavaScript is after you! kertoo, miten se käy. En todellakaan aio perehtyä yksityiskohtiin... ;-)