TDD och ninjan
Jag hittade en version av boken Secrets of the JavaScript Ninja och började läsa lite i den som en kick-off för steg 2.. I början utav boken står det om TDD och en enkel testfunktion som ser ut så här:
function assert( value, desc ) {
var li = document.createElement("li");
li.className = value ? TEST_PASS_LI_CLASS : TEST_FAIL_LI_CLASS;
li.appendChild( document.createTextNode( desc ) );
document.getElementById( TEST_RESULT_ID_LIST ).appendChild( li );
}
Så jag blev sugen att testa lite grann med denna och den var riktigt nyttig tycker jag – jag har aldrig jobbat med testdriven utveckling i Javascript innan. Men med en sån funktion kan man göra det väldigt enkelt och som ändå ger resultat. Följ denna länken för att själv se och testa funktionen.
Ninjan och function properties
Åhå! Till en funktion kan man spara en variabel (eller property) och det ser jag som en väldigt stor nytta.. tänk scenariot att man har en funktion som ska spara status och vid nästa funktionsanrop använda den sparade statusen. Så istället för att spara till en global variabel eller en variabel för ett helt objekt så sparar man det till funktionen som har ansvaret. Niiiice =)
Som i ninja-objektet ska det sparas gamla steg som har genomförts kan man göra så här:
var ninja = {
attack:function(steps) {
// any history array
if (ninja.attack.history == undefined) {
// create one
ninja.attack.history = new Array();
}
// save steps
ninja.attack.history.push(steps);
...
Följ denna länken för ett att se och testa koden.
Singleton eller statiskt objekt
Vidare i min resa så läser jag om singleton i boken Pro Javascript Design Patterns eftersom det är nått jag hört om från flera håll.. har för mig nån kurskamrat nämde att nån i kursledningen pratat om singleton också. Men iaf! Det visade sig att jag visste redan var det var.. fast under namnet statiskt objekt.
Syftet med singleton eller statiskt objekt är att det bara ska finnas en instans av det. Tittar man på koden i rubriken ovan så använder jag just det =)
Recursion
Jag använder detta väldigt sällan faktiskt, oavsett programmeringsspråk, men jag vet vad det är och vad man kan göra. Brukar inte springa på situationer där det är användbart.
Men i kursen Webbprojekt II så dök det upp en användbar situation i förra veckan. Det var vid en synkning så söker koden igenom en mapp efter en inställningsfil (xml) men nu skulle en del plugins (eller widgets som vi säger) ligga i en undermapp. Då gjorde jag en liten refactoring i huvudfunktionen och la ut den kod som hämtade xml-filen så att den kan återanvändas. Sen i huvudfunktionen la jag till ett test om det var en undermapp och då kördes en inre loop som sedan kallade den nya funktionen som läste in filen.
Vid närmare eftertanke så kanske inte det är ett praktexemplar i recursion då recursion innebär att funktionen kallar sig själv och iom detta går en nivå djupare.
Men enkelt beskrivit så är recursion en bra sak om man ska skriva en intelligent funktion som ska hantera flera olika möjligheter, om funktionen ska kunna hantera flera nivåer.
Jag brukar istället hamna i situationer med nästlade loopar fast där den nästlade loopen har andra villkor än den yttre loopen – om den inre loopen haft samma villkor som yttre loopen hade man kunnat kalla funktionen igen.
Arguments
Det här visste jag faktiskt inte om i Javascript men jag vet att det finns i många andra språk.. om man skriver ett commandline-program eller ett bash-script så vet jag det är vanligt att man använder arguments för att ta in data och agerar sedan på hur det ska fungera.
Jag skulle vilja säga eller förklara att arrayen arguments är en tvist eller ett annat alternativ mot att en funktion tar en array eller en sträng/värde som parameter.
Ta en kik på koden här eller på JSFiddle.
Ser man på användningen på dom två olika sätten som ändå gör samma sak så är det beroende på kodstil och vad man föredrar. Sätter man ihop en array tidigare i koden så kan det vara fördelaktigare med att funktionen tar en sträng/värde eller en array av strängar/värden. Personligen så tycker jag nog bäst om det sistnämnda därför att då ser man i funktionen vad den har för parametrar och man kan specifiera på ett annat sätt med JSdoc eller PHPdoc.
Namespaces
Det här är en väldigt nyttig sak för javascript så att det inte bli namnkrockar. För javascript så är namnrymder helt enkelt en utökning utav singleton och det ser ut så här:
/* Namespace to put Assassins Brotherhood in */
var AssassinsBrotherhood = {};
/* Create the ninja inside the namespace */
AssassinsBrotherhood.Ninja = {
// properties of the ninja
isTrained: false,
weapons: null,
// functions that the ninja has
attack:function(steps) {
...
},
train:function(weapon, time) {
...
}
};
/* New sub-group/namespace within AssassinsBrotherhood */
AssassinsBrotherhood.Weapons = {};
/* Weapons the ninja has available */
AssassinsBrotherhood.Weapons.Shuriken = {
// properties
isSharp: false,
// functions
sharpen:function(){
...
}
};
AssassinsBrotherhood.Weapons.Katana = {
// properties
isSharp: false,
// functions
sharpen:function(){
...
},
slash:function() {
...
}
};
.. fast i det här exemplet vore det bättre med en kombination av singleton och prototype inheritance eftersom förmodligen vill AssassinsBrotherhood ha fler än en ninja och fler än ett vapen
Prototypes
Såååå fortsättar man på coola gänget Assassins Brotherhood och bygger om till prototypes så blir det detta resultat. Alternativt ta en kik på JSFiddle.
Det jag lärde mig eller inte kom ihåg var att man måste använda instanceof för att testa om ett objekt är av en viss typ – det funkar inte att använda typeof därför att den retunerar alltid object.
Context, scope och this
Det jag tyckte var nyttigt var att lära sig skillnaden i hur olika typer funkar.. om det är ett singleton så blir scope (this) just det här statiska objektet men om det är en global funktion så blir scope (this) samma sak som window-objektet. Detta blir även en liten fördjupning till min tidigare rubrik “Ninjan och function properties” =)
I det här exemplet eller på JSFiddle så är katana1 ett singleton och där kan man se att det inte blir globalt genom att window.isSharp blir undefined. Medans i katana2 som är en global funktion så fungerar window.isSharp.
Partial applications
Detta innebär att man skapar nya funktioner dynamiskt och sparar parametrarna som skickas till den. John Resig har en bra beskrivning med exempel på sin sida vad detta är för något men det enda jag tänker på är att det känns överkomplicerat. Det bästa en programmerare kan lära sig är KISS – Keep It Simple Stupid
Men vem vet.. kanske är det något som jag får användning för senare men som sagt; känns som om jag inte har nån användning för det nu.
Closures
Detta är tätt sammanbundet med context och scope. Det är en funktion med hur Javascript funkar i sig.. Genom closures kan man t.ex binda variabler genom att spara ett inkommande värde och sen skapar man en ny funktion innuti funktion som använder den nya variablen. Detta är en nödvändig sak ibland för att Javascript ska “komma ihåg” variabler. Man kan även använda closures för att skapa privata variabler som då ligger innuti en funktion i funktionen.
Här kommer ett exempel:
function addData(id) {
jQuery.get('http://www.server.se/get.php?id=5',
function(data) {
// here a closure is created in the successfunction
// that binds the variable id from addData
jQuery('#'+id).html(data);
}
)};
}
// call a function with a parameter to a div
addData('div-tag');
Så det är ett vanligt exempel med hur man gör i jQuery till exempel.. och som bekant är jQuery inget nytt för mig
Prototypal inheritance
A mouthful.. eh?
Eftersom varje objekt och funktion har en egenskap som heter prototype som är ett tomt statiskt objekt så kan man byta ut detta mot ett annat objekt vilket gör att man kan skapa arv ifrån andra objekt. Säg att vi har en grundklass som heter Person och som har lite nyttiga funktioner som den nya klassen Ninja ska ärva ifrån så kan man göra som i detta exemplet; ta en kik här eller på JSFiddle.
Slutsats
Vad jag behöver komma ihåg och tänka på…. om jag jobbar på ett större javascript-projekt så ska jag komma ihåg att tänka på hur ska strukturen fungera – räcker det med ett statiskt objekt/namespace eller är det objekt som ska kunna instansieras flera gånger?
Jag tycker att det är kul att bygga ihop nån med prototypes och även med arv.. så får klura ut nånting bra där jag kan använda mig av prototypes i projektet för kursen därför att det är inte så ofta att jag kodar ihop nånting där det behövs. Vi använder prototypes nu i vårt webbprojektet men det jag skrev ifrån början så körde vi med statiska objekt.
Generellt sett tycker jag att detta steget har varit en del nytt men mycket har varit repetition med saker som jag kunde eller kände till.