Pasakyk, laikrodėli, ar ne laikas kavą gerti? (1996.07.21)

Praeitą kartą Vartiklyje pažvelgėme į dažniausiai naudojamą 'JavaScript' objektą, - eilutes. Tačiau iš 'Java' pasiskolintas ir „Date“ objektas, kurį galima naudoti veiksnaus laiko ir datos nustatymui. Šiame 'Vartiklio' WWW puslapyje jis naudojamas laikrodžio realizacijai ir nurodymui, kiek jau minučių praėjo nuo prisijungimo prie paskutinio 'Vartiklio' numerio.

'Javascript' traktuoja „Date" objektą kaip konstruktorių, - pradžioje reikia sukurti naują „Date" objektą, o po to taikyti įvairius „Date“ metodus, kad gautume ar priskirtume įvairias reikšmes (ir „Date“ objektas neturi charakteristikų - 'properties'). Dažniausiai naudojami metodai prasideda get („gauti“):

getHours() - gauti veiksnios valandos reikšmę;
getMinutes() - gauti veiksnios minutės reikšmę;
getSeconds() - gauti veiksnios sekundės reikšmę;
getYear() - gauti veiksnių metų reikšmę ("96" reiškia "1996");
getMonth() - gauti veiksnaus mėnesio reikšmę ("0" reiškia "Sausis");
getDate() - gauti veiksnios mėnesio dienos reikšmę;
getDay() - gauti veiksnios savaitės dienos reikšmę ("0" reiškia "Sekmadienis");

Naują „Date“ objektą galima sukurti keliais būdais. Veiksnaus laiko reikšmę saugantį objektą sukuriame nenurodę jokių parametrų:
var Dabar = new Date();

Tačiau galima nurodyti ir konkrečią laiko reikšmę:

var NaujiMetai = new Date("January 1 1997 00:00:00");
var NaujiMetai = new Date(97, 0, 1, 12, 0, 0);

Miegant bug 255 „Date“ metodas taikomas prieš tai sukurtam objektui, pvz.:

var Dabar = new Date();
var Metai = Dabar.getYear();

Nukrypimai ir pavojai

Nors „Date“ objektas ir labai viliojantis naudojimui, jo realizacijoje yra keletas klaidų, o kai kurios iš jų nuostabiai padeda „nusmigti“ „Netscape Navigator“ peržiūros programai.

Pvz., „Macintosh 2.0“ versijoje „Date“ laikrodis skuba viena diena, todėl šį skirtumą galima kompensuoti taip:
Dabar.setDate(Dabar.getDate()-1);

Tačiau tokį „prijomą“ (gudrybę) reikia nukrreipti tik „Mac“ skaitytojams Terpės ir peržiūros programos versijai nustatyti galima naudoti navigator.userAgent  charakteristiką, tačiau paprasčiau pasinaudoti tokia „apgaule“:

var ArMacData = new Date (0); // Priskirti Jam 1, 1970
if (ArMacData == 86400000)
   Dabar.setDate(Dabar.getDate()-1);

Datos iki 1970-ųjų gali „nusmigdinti“ „Netscape“. Aišku, geriausia išeitis yra nenaudoti šio laikotarpio datų. Tačiau, jeigu leidžiama pačiam skaitytojui įvesti datas, reikia tikrinti, ar jis neįvedė „draudžiamų“ reikšmių.

Problemos su 2000-aisiais

"Netscape" nepatinka ir kito šimtmečio datos. Tačiau šį laikotarpį galima „apžioti“, jei naudojamos tik skaitinės datos reikšmės, o ne eilutės, pvz.:

var NA1 = new Date ("January 1, 2000");   // Sminga
var NA1 = new Date ("100,0,1");           // Viskas veikia puikiai

„JavaScript“: funkcinė ir ne visai funkcinė (2021 m. sausis)

Funkcinio programavimo paradigma paskutiniais metais labai išpoliarėjo. Tai, kad „JavaScript“ galime naudoti funkciniu stiliumi, neturėtų stebinti, nes ji vystėsi nuo „Scheme”, kuri yra funkcinė kalba. Tačiau turimi skirtingi funkcinio programavimo „lygiai“, priklausomai nuo to, kaip griežtai norime laikytis jų pagrindinių principų.

Funkcinės kalbos bando supaprastinti kalbas priartinant prie matematinių funkcijų. Kai naudojate matematinę funkciją, jos rezultatas niekada nesikeičia tiems patiems argumentams, pvz., sin(0.3) visad bus ta pati. Tai galima suprasti kaip reikšmės paėmimą iš lentelės – ir net jei reikšmė paskaičiuojama procedūriškai, tai teoriškai visas matematines funkcijas galima realizuoti lentelių pagalba. Funkcijos pakeitimas nuo skaičiavimo prie lentelės dažnai pavadinamas memorizacija (įsiminimu).

Taip nėra procedūriniame programavime, kur funkcija tėra procedūra, gražinanti vieną reikšmę (nors ir sudėtingos struktūros objektą). Bendrai imant, funkcijos gali priimti ir gražinti objektus (apimant ir funkcijas). Tai paprastai išreiškiama kaip „pirmosios klasės funkcijomis“. Be abejo, „JavaScript“ yra tiesiog objektai, kurios gali būti iškviečiamos iškvietimo operatoriumi () – tad jos iš tikro priklauso pirmajai klasei. Įprasta funkciją, priimančią ir gražinančią kitą funkciją, vadinti „aukštesnio lygio funkcija“.

Funkcija gali turėti „šalutinių poveikių” ir būsenos pokyčių, pvz.,

function addone() {
  globalvariable = globalvariable + 1;
  return globalvariable;
}

Šiuo atveju funkcija skaičiuoja, tad ji yra procedūrinė, ir ji negali būti leistina funkcinėje kalboje. Be to ji keičia globalų objektą ir todėl sukelia šalutinius poveikius. Jei kuri kita funkcija naudoja tą globalų kintamąjį, jos elgsena gali pasikeisti. Visad tą pačią reikšmę tiems patiems parametrams gražinanti tą pačią reikšmę ir neturinti šalutinių poveikių vadinama gryna funkcija. Funkcinėse kalbose tokio tipo kaip pavyzdyje funkcijos neleidžiamos, o „kintamieji“ yra nekintantys (immutable) – kartą sukurtas objektas negali keistis. Tai kaip tada gali veikti ciklai ir sąlygų patikrinimai? Kaip realizuoti skaičiavimus atliekančias funkcijas?

Yra du atsakymai. Pirmas – kodėl mes iš viso skaičiuojame? Jei skaičiavimas yra algoritmo dalis, kuri duoda jums sprendimą, tai neskaičiuokite - tiesiog atiduokite sprendimą. Pvz., jei iteratyviai skačiuojate sin(0.3) reikšmę, tiesiog paslėpkite iteraciją funkcijoje ir gražinkite rezultatą (iteraciją palikite nefunkcinėms realizacijoms). Kitas atsakymas yra funkcinių būdų tų pačių veiksmų atlikimui – daugiausia per rekursiją ir funkcinę kompoziciją. Pvz., jei f(x) yra x+1 gražinanti funkcija, tada paskaičiavimas gali būti realizuotas taip: z=f(f(x)) gražinant x+2 ir t.t. Yra ekvivalentų ir ciklams, naudojant funkcinę kompoziciją arba rekursiją. Pvz., ciklinimui nuo N iki 0 galite panaudoti funkciją

function decreaseByOne(N) {
   if (N === 0) return;
   count(N-1);
}

Tad grįžtant prie “JavaScript”, ji turi „pirmosios klasės funkcijas“, leidžiančias funkcinę kompoziciją, ir primityvias aukštesnio lygio funkcijas. Tačiau didžiausia problema lieka šalutiniai efektai, liekantys norma, kaip ir objektų kintamumas. Ir net įtrauktos lambda funkcijos nepadaro jos labiau funkcionalia (nors šios ir paveikė funkcinį programavimą). Vis tik ji leidžia funkciškai orientuotą programavimą (FOP).

Aukstesnio lygio funkcijų galimybę suteikia galimybė funkcijas perduoti kaip parametrus bei gražinti funkciją. Kas programavo su „JavaScript“, greičiausiai susidūrė su aukštesnio lygio funkcijomis callback kontekste.

Štai, pavyzdžiui, jei norime parodyti, kiek truko funkcijos vykdymas, galime pasirašyti tokio tipo apvalkalą bet kokiai funkcijai:

function timeFunction(f) {
   return function (...args)
     { 
       var t1 = performance.now();
       var result = f(...args);
       t1 = performance.now() - t1;
       console.log("Vykdyta: " + t1);
       return result;
     };
}

Svarbus momentas čia yra parametrų perdavimo operatorius (...), sukuriantis masyvą priklauomai nuo to, kiek parametrų buvo perduota. Taip galima sukurti aukštesnio lygio funkcijas, priimančias bet kokį kiekį parametrų. Pvz., galite sukurti funkciją trijų skaičių sudėčiai ir perduoti ją timeFunction dunkcijai:

function sum(a, b, c) { return a + b + c; }
sum= timeFunction(sum);
console.log(sum(1, 2, 3));

Ko gero, geriausiai žinomos ir dažniausiai naudojamos funkcijos yra map, filter, reduce. Jos priima funkciją kaip parametrą ir ją pritaiko kiekvienam rinkinio elementui.

Tiesiog - Java
JavaScript eilutės
JavaScript pradžiamokslis
Java 8: Optional prieš null
Iš kur Javos tas lėtumas?
Programavimo kalbų istorija
Lambda išraiškos: Java į naują lygį
Pirmasis „Java“ įskiepis Lietuvoje
Pitonas, kandantis sau uodegą!
Dygios JavaScript eilutės
Lyginant su gimtąja kalba
Advanced HTML skirsnis
Anotacijos Java kalboje
Unix komandinės eilutė
Vartiklis
Tcl kalba