JavaScript kitoniškumas

JavaScript yra unikali tarp kitų kalbų. It ji yra pilna programavimo kalba, leidžianti spręsti įvairius uždavinius.

Objektai, o ne klasės

Tačiau labiausiai programuotojus suerzina tai, kad joje nėra klasių. O kitos kalbos, ne tik turi klases, bet ir griežtą tipų kontrolę, Tačiau to visai nereikia JavaScript kalbai!

Juk JavaScript turi objektus – kai kuriuos pateikiamus, tačiau, daugiausia, susikuriamus. Ir ne pirma kuriate klasę, kurią po to inicializuojate, o paprasčiausiai imate ir kuriate objektus. Pvz.,

var oMano = { };     
oMano.naujasMetodas = function() { };
oMano.naujaSavybe = "duomuo";

O galima naudoti new Object konstrukciją. Ir visai nesvarbu, kaip sukuriamas objektas, - ir nėra poreikio klasės sąvokai.

Bet kodėl taip dažnai bandoma į JavaScript įtraukti klasės idėją? Tam yra trys priežastys. Pirma, klasės aprašas naudojamos skirtingų objekto kopijų (instances) sukūrimui. Antra, klasė naudojama tipų apibrėžimui, o trečia – klasės pagalba apibrėžiamas paveldimumas.

Visos trys yra visiškai nebūtinos ir yra skirtos aprašo dalies atskyrimui nuo vykdymo. Interpretuojamoms kalboms tam nėra poreikio. JavaScript kalboje objektai sukuriami vykdymo metu. Peržiūrėkime tas tris klasių priežastis.

Dėl daugelio objektų kopijų, JavaScript kalboje dauguma objektų yra vienišiai ir todėl nebūtinas mechanizmas jų daugelio kopijų kūrimui. O paprasčiausias būdas daugelio kopijų sukūrimui – panaudoti gamyklinę funkciją. Juk jei yra objektai, tai galima įsivaizduoti, kad yra gamyklų objektai, kuriantys kitus objektus. Pvz.,

manoObjektuGamykla = function () {
     var oLaikinasObjektas = { };
     oLaikinasObjektas.manoMetodas = function() { 
           alert(oLaikinasObjektas.manoSavybe); } ;
     oLaikinasObjektas.manoSavybe = "duomuo";
     return oLaikinasObjektas;
}

var o1 = manoObjektuGamykla();
var o2 = manoObjektuGamykla();

Pastaba: yra keletas būdų objektų gamyklos sukūrimui.

O dabar dėl tipų. Tačiau JavaScript turi objektus su metodais ir savybėmis, kurie įtraukiami vykdymo metu. Tai dinaminė kalba, kuri sukonstruota taip, kad joje tipo sąvoka yra beprasmė. Ir tai gerokai supaprastina kalbą, nes nereikia rūpintis hierarchijomis, tipų suderinamumu ir konversija ir pan.

Pagaliau – paveldimumas. Bet jei nėra tipų, nėra ir tipų hierarchijos. JavaScript kalboje paveldimumas pakeičiamas pragmatiškai įtraukiant ar pakeičiant metodus ir savybes.

Trumpai tariant, JavaScript gyvuoja vykdymo metu!


Dygios eilutės

“JavaScript” viskas yra objektai ir joje labai lengva įtraukti funkcijas, manipuliuojančias pagrindiniais duomenų tipais Pvz., net paprasčiausia eilutė yra objektas, todėl galime parašyti (kas prieštarauja visiems programavimo dėsniams):
 alert("ABCD".length);

Turėdami tai mintyje, pasirašykime trumputę funkciją, palyginančią, ar nurodyta eilutė yra lygi norimai teksto eilutei:

 function arTaip(eil) {
   return eil === "taip";
}

Pirmiausia, tai yra tikslus tikrinimas – eilutė privalo būti lygi tik „taip“ ir jokiais būdais nekonvertuojama į „taip“ iš kitų duomenų tipų. Tad nėra didelio skirtumo tarp „==” ir „===“ naudojimo – juk eilutė yra arba nėra eilutė, ir ji lygi arba nelygi „taip“. Tad galime naudoti ir

function arTaip(eil) {
   return eil == "taip";
}

Tikėdamiesi tų pačių rezultatų, nors bendrai priimta laikyti, kad reiktų vengti „==” ir „!=“, tad pirmas variantas labiau vartotinas.

Taip, tačiau toji funkcija buvo naudota daug didesnėje programoje, - ir buvo pastebėta, kad kartais, funkcijai perdavus „taip“, ji netikėtai gražina false. Tada funkcija buvo papildyta pora patikros eilučių:

 function arTaip(eil) {
   alert(eil);
   alert(eil === "taip");
   return eil === "taip";
}

Ir tada vieno kreipinio metu funkcija gražino „taip“ ir „false“! Kas čia vyksta!

Priežastis - operatoriaus „===” veikime. Parašykime tokį testavimo kodą mūsų funkcijai:

var E1 = "taip";
arTaip(E1);
var E2 = new String("taip");
arTaip(E2);

Pirmosios eilutės atveju gausime atsakymą true, o antrosios - false. Mat „===“ tikrina tapatumą, kuris yra daugiau, nei vien reikšmė. Abu argumentai turi būti nuorodos į tą patį objektą. E2 yra kitas eilutės tipo objektas nei "taip" objektas. Įsitikinimui, papildykime mūsų testinį kodą tokiomis eilutėmis:
var E3 = new String("taip");
alert (E2 === E3);

Atsakymas bus, kad E2 ir E3 yra nelygūs! Tačiau padėtis „pasitaiso“, jei bus panaudotas priskyrimo operatorius:
E3 = E2;
alert (E2 === E3);

nes šįkart patenkinama tapatybės patikra. Dar įdomiau, kad false gausime ir naudodami „==“ operatorių (kai tikimės tik reikšmių sutapimo palyginimo) su skirtingų objektų kintamaisiais. Tuo tikslu išbandykime naują testinį pavyzdį:

var E2 = new String("taip");
var E3 = new String("taip");
alert (E2 == E3);

Čia problema tame, kad JavaScript neturi aiškaus reikšmės tipo, į kurį automatiškai konvertuotų eilutės tipą (kitaip sakant, nemoka „String“ tipo objekto versti į primityvųjį eilutės tipą). Ir jei jau manote, kad viskas toliau aišku, tai mažas testas jums – nuspėkite (mintyse) šio (žemiau) fragmento rezultatus!

var E2 = new String("taip");
var E3 = new String("taip");
alert (E2.toString() === E3);
alert (E2.toString() == E3);

Na? O dabar patikrinkite su kompiuteriu... Atspėjote?!

Pirmu atveju yra false, o antru true. Ir čia galite vietoje „toString()“ panaudoti bet kurį kitą eilutės metodą, pvz., „toLowerCase“ – rezultatas bus tas pats (false ir true). Taip yra todėl, kad visi eilutės tipo metodai gražina primityvųjį reikšmės tipą, o ne „String“ tipo objektą.

Ir nesvarbu, ką rašo vadovėliuose, eilutės konstanta yra objektas kito tipo, nei „String“. Jei eilutės konstanta panaudojama tarsi ji būtų „String“ objektas, ji, prieš panaudojimą, automatiškai konvertuojama į „String“ tipo objektą. Todėl programose galime naudoti eilučių konstantas tarsi jos būti „String“ tipo – tačiau taip nėra!

Ir galutinai sumaištį sukelia faktas, kas jei „String“ objektą sukursime nenaudodami konstruktoriaus (t.y., be „new“), gausime primityvųjį eilutės tipą (t.y., eilutės konstantą). Tad toks pavyzdys grąžins true (taigi new panaudojimas iššaukia didelį skirtumą):
var E2 = String("taip");
arTaip(E2);

Hm, ar tikrai galvojote, kad tiek niuansų gali būti lyginant dvi teksto eilutes? Ir nelengva duoti patikimą patarimą, šio tipo problemų išvengimui. Pirma, jei viena iš lyginamų eilučių yra konstanta, saugiau naudoti „==” (nepaisant rekomendacijų). Jei nesate tuo tikri, tada problema gerokai sudėtingesnė, ir geriausias apsidraudimas būtų naudoti taip:
x.toString() == y.toString() (čia jau nesvarbu, „==” ar „===” – ar išmąstysite, kodėl?). Tačiau net ir šiuo atveju išlieka nedidelė problemėlė tuo atveju, kai x ir y yra bendresni objektai, t.y. ne eilučių objektai.

Ir tai nėra klaida JavaScipt realizacijoje – tokia problema yra bendra visoms kalboms su silpna tipų kontrole bei dinaminiais objektais. Kai į žaidimą įtraukiamas automatinis objektų reikšmių konvertavimas, iškart kyla potencialus pavojus.

Ankstesnės "Advanced HTML" skyrelio temos:
JavaScript atspindžiai"
Pelė uodega švystelėjo...
Sveikųjų skaičių žaidimai
AWK kalba - sena ir nuolat aktuali
CGI.pm biblioteka: sausainiai
Ką delne mums neša HTML 4.0?
Lambda išraiškos – Java į naują lygį
Kaip valdyti piešinių pakrovimo tvarką
Kaip lankytoją nukreipti į kitą WWW puslapį
Bilas Geitsas: kol dar nebuvo garsus
Java 8: Optional prieš null
Viešojo rakto kriptografija
Tikroji Interneto pabaiga
Įlįskite į lankytojų kailį
Vaizdi rašysena - VB Script
ASP patarimų liūnas
Debesies architektūra
Tiesa apie REST
Tcl kalba

JavaScript pradmenys
Anotacijos Java kalboje
Viešojo rakto kriptografija
Nutylimųjų savybių ieškant
Tiesiog - Java
Vartiklis