Näytetään tekstit, joissa on tunniste OOP. Näytä kaikki tekstit
Näytetään tekstit, joissa on tunniste OOP. Näytä kaikki tekstit

24 joulukuuta, 2014

Todellinen datatyyppi

Muuttujan arvon datatyyppi

Ongelma ja ratkaisu

Kuinka voi selvittää mikä muuttujan arvon todellinen datatyyppi, riippumatta siitä, miten se on muodostettu? Normaalisti tämä on ihmeellisen hankalaa JavaScriptissä, sillä esimerkiksi numeron voi luoda kahdella tavalla: 

var n = 2; 
var N = new Number(2);

Molemmat ovat kakkosia ja molemmilla voi laskea aivan normaalisti, mutta jos niitä vertaa keskenään ne ovatkin ihan eri asioita:

if ( n === N ) { // jne

Saa vastaukseksi false. Miksi? Koska toinen on primitiivi ja toinen on objekti. 

Yksi ratkaisu olisi tietty käyttää löysempää vertailua, joka kääntää molemmat arvot samaan tyyppiin ennen vertailua, mutta silloin samalla luo potentiaalisesti vaarallisen tilanteen, jossa saman numeroarvon omaavaa muuttujaa ovat aidosti erilaisia datatyypiltään. 

if ( n == N ) { // jne

Sama ongelma käänteisenä on Objektin ja Arrayn välillä: jos kysyt typeof sanalla:

var MyArr = [];
var MyObj = {};
if ( ( typeof MyArr ) === ( typeof MyObj) ) { // true !

Molemmat ovat tyyppiä object, vaikka ne pitäisi nimenomaan pystyä helposti erottamaan toisistaan. Ja samaa tyyppiä ovat myös Date, RegExp ja Math jne...

Hämmentävä perusongelma JavaScriptissä on syntynyt pitkän historian aiheuttamana, mutta ratkaisukin on kuitenkin jo olemassa. Ongelma pitää vain tiedostaa: Toisaalta pitäisi erotella sen mukaan mitä datatyyppiä on sisällä, ja unohtaa ulkoinen objektin tyyppi, toisaalta yleensä eri objektityypit (ainakin Array ja Object ) pitäisi erottaa toisistaan selkeästi. Mitä tehdä? 

Vastaan: Pitää luoda funktio, joka kertoo datan oikean tyypin.

Ratkaisu on realDataType -funktio

Se yksinkertaisesti kertoo mitä dataa sille syötetään. Ensin switch tarkistaa, ettei arvo ole jokin tyhjistä (null tai undefined) , sitten katsotaan vain tyypin prototyyppi, ja halutessa (asettamalla toinen parametri true -asentoon) tarkistetaan Number -primitiivi vielä tarkemmin sen mukaan onko kyseessä kokonaisluku vai liukuluku. 

Lisäksi samalla voidaan tarkistaa onko luku käypä numero. Jos arvo on NaN tai se on liian suuri tai pieni, tyypiksi tulee jotain muuta kuin Number, mikä mahdollistaa helpon testin. Tämä on kuitenkin tehty optioksi, jonka saa halutessaan käyttöön true -lisäparametrilla.

var realDataType = function (val, separateNumbers) { /* 'real' datatypes */
 var sn = ( separateNumbers || false); 
 switch (val){
  case undefined: return "undefined";
  break;
  case null: return "null";
  break;
 default:  
var proto=Object.prototype.toString.call(val).match(/^\[object (.+)\]$/)[1];
  if("String" === proto) {
return (/^(0x|0X|#)[0-9a-f]{3}(?:[0-9a-f]{3})?$/i.test(val))?"Hex":"String"; 
  } // string
  if("Number" === proto){
   if (isNaN(val)) return "NaN"; // epäluku
   if (isFinite(val) || (val < Number.MAX_VALUE && val > -Number.MAX_VALUE)) { //"ok";
    if (sn) {
      return (val % 1 === 0) ? "Integer" : "Float"; 
    }else{
      return "Number";
    }
   } else { // too big or small:
    if ( val > Number.MAX_VALUE) {
     return "Infinity";
    } else if (val < -Number.MAX_VALUE){
     return "-Infinity";
    };
   };
  }else {
    return proto;
  }
 }; //switch end
};

alert( realDataType( Number.MAX_VALUE )); // Number
alert( realDataType( Number.MAX_VALUE, true )); // Integer
alert( realDataType( Number.MAX_VALUE * 2 )); // Infinity
alert( realDataType( "#00FF00" )); // Hex
alert( realDataType( /[a-zA-Z]/ )); // RegExp

Ohjelmoitu nörtin joululahja

Samalla logiikalla lisäsin vielä merkkijonon tunnistamisen hexadesimaaliksi.
Suosittelen: ota tästä kätevän ilmainen työkalu ohjelmoijan pakkiin. Jos tunnet jonakin päivänä olevasi kiitollisuudenvelassa, ota yhteyttä, lähetän tilinumeroni. Ei, olkoon tämä minun joululahjani koko ohjelmoivalle maailmalle.

Olen itse surfannut vuosien varrella melkoisen paljon ilmaisia ohjeita läpi, päätyäkseni tähän ratkaisuun ja muillakin vastaavia ratkaisuja on olemassa jo valmiina, mutta niissä ei sama funktio tutki esim. numeroita näin pitkälle.
Joten olkaapa hyvä ja mukavaa uutta vuotta!
On siinä varmasti vielä kehittämisen varaa, jos keksit, kerro minullekin. 

Float vai Real?

Oli vaikea päättää tätä: Float tarkoittaa liukuluku, oikeastaan se on kuitenkin Real eli reaaliluku, mutta onko se sitten amerikkalaista kulttuuria vai mitä, mutta tuntuu tutummalta käyttää tätä, en tiedä, voin olla väärässäkin.

Tulevaisuuden perusta on tässä

Funktio toimii todennäköisesti myös kaikkiin tulevaisuudessa kehitettäviin uusiin datamuotoihin. Tässä esimerkki, jota en vielä kehitellessä hoksannut ottaa testattavaksi, mutta sieltähän se datatyyppi tulla tupsahtaa. Arguments ei siis ole tyypiltään Array. Vaikka se osittain osaakin samoja temppuja, ei kuitenkaan kaikkia.

var b = function (a){
alert(realDataType(arguments)); // Arguments
}();

02 joulukuuta, 2014

Osa 4 minimi app JavaScript

Minimi JavaScirpt -web-ohjelma (app)

Ensinnäkin mini app tarvitsee kääreen, eli kuoren tai kapselin sisuksilleen. OOP:ssä, eli objekti - orientoidussa ohjelmoinnissa on kyse juuri tästä. Valtaosa objektin älykkyydestä on piilossa paketin sisällä ja 'esiin' jää vain ohjelman hallintaan tarvittava käyttöliittymä.

Tehdään siis (triviaali) pieni objekti, jolla on yksityinen salaisuus muuttujassa ja pari funktiota joilla arvo voidaan asettaa ja lukea. Jotta tieto olisi turvassa, se pitää voida varmistaa (validate), ennen kuin se viedään perille asti. Oikea hyötyohjelma olisi paljon laajempi, mutta nyt ollaan hahmottamassa perusasioita.

var app = (function () { // app & suojattu alue alkaa
var secret,
privateSetter = function (x) {
alert( "tutkitaan:" + x);
secret = x;
return true;
}; // suojattu alue loppuu
return { // julkinen alue alkaa = interface
'get': function get () { // getter
return secret;
},
'set': function set (c){ // setter
return privateSetter (c);
}
    }; // julkinen alue loppuu
} () ) ; // app loppuu

Käydään läpi osa osalta, ulkoa sisään.

Muuttuja, jossa koko ohjelma asuu on nimeltään app.
var app = ();

Seuraavaksi on kääre -funktio. Perässään sulkeet jotka laukaisevat funktion heti.
function (){}()

Julkinen osio: return {}; Palauttaa objektin, joka sisältää pari funktiota, (aksessorit) 
getter ja setter:  function get(){}, function set(){}
Vain nämä kaksi funktiota, eli oikeastaan objektin metodia näkyvät ulospäin koko app -objektista. Kaikki muu on suojattuna (private) muuttujissa, eikä näy app -objektin ulkopuolelle. 

Getter ja setter ovat oikeutettuja (privileged) funktioita. 
Yksityinen PrivateSetter -funktio , eli metodi siirtää annetun arvon julkiselta alueelta suojatulle, varsinaiseen muuttujaan, joten tässä on sopiva tilaisuus tarkistaa syötetyn data oikeellisuus. Ei sentään ihan mitä tahansa suostuta tallentelemaan. Varsinainen validaattori tosin tästä toteutuksesta puuttuu, mutta se tulisi siis tähän kohtaan joskus lähitulevaisuudessa, sitten kun tarkemmin tiedetään millaista syötettävän datan pitäisi esimerkiksi olla
Lähiaikoina näytän täällä esimerkiksi miten arvon datatyyppi tarkistetaan.

Huomaa, että funktion palautusarvona on true. Sen voi vaihtaa tai poistaa. Voisi olla kätevää vaikkapa tietää, mikä oli muuttujan edellinen arvo, jos sen haluaa esimerkiksi perumistoimintoa (cancel) tms. varten laittaa jonnekin muistiin jne. 

Varsinainen testi alkakoon

Jos ohjelma on kasattu ja saatu selaimeen, voidaan aloittaa.
Kirjoita seuraavat testikoodit app -objektin jälkeen:

Ensimmäiset testikoodit, joilla näkee että ohjelma ehkä toimii:
app.set("Ohjelma toiminnassa!"); // tutkitaan:Ohjelma toiminnassa!

Asetetun arvon voi lukea get -funktiolla:
put(app.get()); // Message: Ohjelma toiminnassa!

Var secret on turvallinen muuttuja. Jos yrität lukea sen suoraan muuttujasta se ei onnistu.
Asetetaan ensin virallista tietä uusi arvo muuttujaan:
app.set('ABRAKADABRA'); // tutkitaan:ABRAKADABRA

Yritetään sitten saada argo ulos muuttujasta ilman getter funktiota:
alert("UGH:" + app.secret); 
// UGH:undefined - Ei onnistu ! Piilossa pysyy !

Yritetään sitten ylikirjoittaa vanhan datan päälle oikoreittiä:
app.secret = "foo"; // siitäs sait, senkin...

ja lukea se uudelleen... 
var temp = app.secret = app['secret']; // kumpi tahansa tapa käy
alert("secret:" + temp); // secret:foo 

/* Mitä, foo?! Onnistuiko ylikirjoitus?!  
Itsevarmuus kohoaa ylimitoitetuksi... Kato äiti ! */

... mutta pian:
/* Virallinen get -funktio: */
alert("app.get:"+ app.get()); 
// app.get:ABRAKADABRA   - Eiii... Minua on petetty... paha ei kannata...

Todelisuudessa kävi niin, että alkuperäisen, salaisen ja sisäisen secret -muuttujan lisäksi, lisäsit objektiin uuden this.secret -muuttujan, joka on eri alueella, kuin alkuperäinen.

Koska ne ovat eri nimiavaruudessa ne eivät oikeastaan mahda toisilleen mitään.
Ohjelman sisäiset funktion eivät yleensä yritä viitata oman objektinsa ulkopuolelle, joten sekaannuksiakaan ei pitäisi tulla. Voit tallentaa tänne vaikka puhelinnumerosi ja silti saat odottaa seuraavaa puhelua aika pitkään.

Tällainen on minimi ohjelmanen JavaScriptillä toteutettuna.
Miten sinä parantelisit sitä? Mitä puuttuu? Mitä on liikaa?

24 marraskuuta, 2014

Osa 3. Unit testausta JavaScript 'strict mode'

Unit testing

Global on nyt undefined

Kun aloitat koodaamaan 'strict mode'ssa, olet varmaankin aloittamassa projektia aivan alusta, kesken ei kannata yrittää. Strict modessa on täysin uudet säännöt. Ensimmäiseksi esimerkiksi vaikkapa tämä: funktion this muuttuja ei enää sisällä global objektia [window object], vaan se on undefined. Tämä tieto sattumalta mahdollistaa helpon boolean -testin minkä tahansa funktion sisällä, jos this on true, se on global object, ja strict mode ei ole päällä. Jos this on false, eli undefined, strict mode on päällä. 

var varmista = function varmista(testi, msg) { 
     if (!testi) { 
          put("Debug:\n" + msg); 
     };
}; // varmista = itse tehty unit testi

var strictMode = function (){ // palautusarvo Boolean
        return !this;
} // strictMode(); 

varmista (strictMode(), "strict mode: " + strictMode()); 
   // testaa strict mode

Pieni unit test, varmista(), helpottaa testausta, vaikka kesken koodin:

var testi = false; // mikä tahansa boolean -testi
var selkokielinenViesti = "Hei: varmista -testin pitäisi olla true, ei false" 
                        // Kerro tässä mitä olisi pitänyt tapahtua
varmista(testiselkokielinenViesti);

Kun ajat testin (false) -arvolla, saat ilmoituksen:

Err: 
Error: Debug: Hei: varmista -testin pitäisi olla true, ei false
Url: 
 file:///Users/who/Sites/js/barebones/js/main.js
Line: 21 / 34

Saat tietää paitsi oman viestisi, sen lisäksi: 
1: Virheen tyypin,jos sellainen löytyy. Yleensä vain Error:
2. URL tiedostoon, jossa virhe esiintyy.
3. Millä rivillä ja monennessako kirjaimessa virhe esiintyy.

Jos testi on true, ei tapahdu mitään, kaikki on hyvin ja voit unohtaa koko testin.

On myös mahdollista muutella koodia siten, että erityyppiset virheilmoitukset käsitellään eri tavoin ja vaikkapa kerrotaan laajemmin selkokielellä miten kyseinen virhe voi syntyä, tai annetaan linkkejä materiaaliin, mutta tämän esimerkin tarkoitus on vain päästä alkuun.

Ideaali olisi, että ensin kirjoitat testin ja vasta sitten koodin jota testaat. Jätät testikoodin paikalleen, jotta jos joskus muutat vahingossa jotakin testi heti huomaa muutoksen ja varoittaa ei-toivotuista vaikutuksista. Sillä mitä nopeammin huomaat virheen, sitä nopeammin se on korjattu. Testaa siis kaikki koodi kauttaaltaan koko ajan, korjaat sekunnissa kun saat tiedon heti. Jos törmäät virheeseen joskus myöhemmin, et varmaankaan enää muista miten se on syntynyt ja metsästys voi olla pitkällinen prosessi. Testaa myös jokainen työkalusi, tee huolellinen perusta, jolle voi huoletta rakentaa.

Perusidea: Tutkimusten mukaan vanhaa koodia ei enää kukaan uskalla korjailla. Mutta jos kaikki on testattua, uskallat halutessasi ehkä jopa muokata jotakin vanhaa koodiasi, kun aika koittaa. Ja jonakin päivänä tämäkin projekti on kasvanut liian suureksi.

Kommentoi 'use strict' -ilmoitus pois hetkeksi ja testaa saatko siitä ilmoituksen:
var xyz = function () { 
// 'use strict'; // kääre alkaa ...

Lataa sivu uudelleen. Nyt pitäisi tulle virheilmoituksia. Tarkistele.
Muista palauttaa kommentit sitten takaisin, jotta pääset varsinaiseen työhön.

Näin on testipenkki valmis varsinaiseen koodin vääntöön...

Olisi siis tarkoitus rakentaa minimi -ohjelma ja testailla samalla perus -OOP koodeja.
Ja kyllä, tietysti 'strict mode' päällä.

17 marraskuuta, 2014

Osa 2. JavaScript Testipenkki

JavaScript koodin testipenkki 'strict mode' -testeihin.

Script tagissa on hyvä olla onerror -parametri, joka ilmoittaa jos kooditiedostosi ei lataudu, jos esim: osoite on väärin, tms. Virheitä voi olla vaikea jäljittää, jos tiedosto ei ole edes latautunut. Ulkoinen kooditiedosto on kuitenkin erittäin hyvä tapa, ei siis ollenkaan HTML -koodin sekaan JavaScriptiä, jos suinkin mahdollista ja yleensä on.

<script src="js/main.js" onerror="alert('Load Err: ' + this.src)">

Luo main.js -niminen tekstitiedosto ja siihen koodi:

var xyz = function () { 'use strict'; // this is wrapper start ...
       // Only 'strict mode' -experiments here !
}(); // strict mode wrapper end

Käytä funktiota irroittamaan koodi globaalista.

1. Ensin sulkeet: (); - Irrottaa koodin global -objektista
2. Anonyymi funktio toimii kääreenä

function () {} ();

3. Ensimmäinen asia koodissa on ilmoitus: 'strict mode' -joka kertoo selaimelle, että tähän koodialueeseen on suhtauduttava hiukan tiukemmin, kuin mihin on totuttu. Jos lause tulee vasta funktion sisällä, sen vaikutusalue on vain kyseisen funktion sisäinen, tuolloin globaali objekti koodataan kuten tavallisesti, eli strict mode ei ole silloin voimassa globaalilla alueella.

4. Virheilmoituskoodit on hyvä koodailla nyt saman tien, kun kerran testipenkkiä tehdään:
   - virheilmoitus kertoo miltä riviltä bugi löytyi, joten löydät sen nopeammin.

Lisätään Try - cach() - finally  -alueet:

try {  /*debug area*/
window.onerror=function(msg,Url,Ln,ch){
 alert("Window error:\n" + msg + "\nURL: " + Url + "\nLine: " + Ln+"/"+ch);
};
///// TESTIALUE ALKAA - TÄSTÄ ETEENPÄIN SAA MUOKATA ///////////

// Teretuloa 'strict mode' -kokeilujen kentälle !

var put = function(w) {alert('Viesti: ' + w ); };
put("Heippa Maailma"); // testataan 'put' -funktiota

///// TESTIALUE LOPPUU - ÄLÄ MUOKKAA TÄTÄ ALEMPAA ///////////
 } catch (e) { //Tämä blokki ajetaan jos virhe tapahtuu
        throw e; //window.error() kertoo virheen rivinumeron!
} finally { //Tämä blokki ajetaan joka kerta
//alert("'main.js' sivu on ladattu!"); 
} // try

Siinä. Tämä artikkeli ei vielä mitenkään erityisesti käsitellyt strict modea, mutta siihen tullaan seuraavassa. Tämä oli tärkeää vaihe, niin on jatkossa hiukan helpompaa.

Unit -testaus olisi seuraava vaihe...



27 lokakuuta, 2014

Osa1 JavaScript 'strict mode'

Miniohjelma JavaScriptillä

Kuinka toteutetaan minimi app -runko modernilla tavalla, siis käyttäen JavaScript:in  ECMA 5.1 standardia ja 'strict mode' :a ?

Luo ensin normaali HTML 5 perussivu koodin lataamista varten:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>StrictModeTestBench, ECMA5.1
    </head>
    <body>
<div id="stage">Hello World Barebone HTML 5
<!-- onerror paljastaa jos tiedosto ei lataudu -->
        <script src="js/main.js" onerror="alert('Latausvirhe: ' + this.src)"></script>

<!-- Lataa koodi selaimeen, jos käteismuisti estää muutokset -->
<a href="js/main.js">Lataa main.js selaimeen
    </body>
</html>

Tallenna pelkkänä tekstinä nimellä index.html, jonnekin mistä sen voi ladata selaimeen. Luo samalla js - kansio koodeille. Sitten koodisivun kimppuun.

26 lokakuuta, 2014

Part 5 'strict mode' JavaScript

Where is callee and caller in strict mode?

In the good old days (yesterday) there was a time when you could call your JavaScript function and that function was so familiar that he/she could know who was the caller, like this:

var foo = function ( ) {
  alert("hi, I am function: " + arguments.callee.name);
  alert(arguments.callee.caller.name + " called, can I help?");
}; // end
foo();


But now we live in ECMA 5.1  'strict mode' and that is just not happening anymore. Really.
If you do use it you will get this kind of TypeErrors:

TypeError: undefined is not an object (evaluating 'this.caller')
TypeError: undefined is not an object (evaluating 'this.callee')

What to do? How can we... yes, relax, there is something we can do, but not anything.
Do not rush into strict mode if you are in the middle of something, but start your next project from scratch with strict mode on!

Not a solution but something
Ok. Let us see. Imagine you have a small app -object with bunch of privileged methods:

'strict mode'

var app = (function () { // app & private area start
var secret,
privateSetter = function (x){
// alert("private: " + x);
secret = x;
return true;
}; // private area end
return { // privileged area start
'get': function (){ // getter
return secret;
},
'set': function (c){ // setter
return privateSetter(c);
}
    }; // privileged area end
} () ) ; // app end

You can get those method variable names like this:

var getMethodNames = function (obj){ // ret: Array of method names
var names = [];
for(var key in obj){
if(obj.hasOwnProperty(key)){
names.push(key);
} // if
} // for end
return names;
} // alert( getMethodNames(app));  // 'get,set'

Method privateSetter is not seen here because it is encapsulated in private area of object
You can get whole source code of any method with:

alert(app['set']);

But it's useless because function is anonymous. But what if we name the functions, they don't have to be anonymous, actually they can have any name, also different than method -variable names. So lets alter the code like this, look bold text after function -words:

var app = (function () { // app & private area start
var secret,
privateSetter = function (x) {
// alert("private: " + x);
secret = x;
return true;
}; // private area end
return { // privileged area start
'get': function get () { // getter
return secret;
},
'set': function SET (c){ // setter
return privateSetter (c);
}
    }; // privileged area end
} () ) ; // app end

Not all functions are anonymous anymore. So, try running following code, getFuncName 
will return the altered (new) name of method.  (SET. With kapital, for clarity here)


var getFuncName = function (obj, key) { // return the name of method
if( obj.hasOwnProperty(key) ) {
return obj[key].name ? obj[key].name : "anonymous";
} else {
return "No_Method_" + key;
}
}
alert(getFuncName(app, 'set')); // SET

If function have always same name as corresponding method variable, this might be useful habit, so then you can ask a function it's name sometimes? But why? If you find yourself in a situation like that ask yourself a question: Why? Why does the poor function have to have a name, and it should even know it? Most functions can be anonymous and happy. So I am not the guy who told you that you should name all your functions. Keep private functions anonymous, please, for securitys sake. But this might be something interesting about 'strict mode' for somebody...
This is something what I am working on, so I might put more about strict mode JavaScript here if anybody is interested... maybe not. How many? Just you, ok. Let me know. See you.