Javascript-perusteet
- Alkuvalmistelut
- Hello World
- Muuttujat
- Debuggaus
- Tietorakenteet
- Silmukat
- If-lause
- Funktiot
- Objektit
- Taulukot ja objektit ovat viitteitä
- Monimutkaisemmat tietorakenteet
- import ja export
Harjoitellaan Javascriptin perusteita. Tehtäviin löytyy myös malliratkaisu (malli.js).
Alkuvalmistelut
- Käynnistä Firefox-selain. Seuraavat tehtävät toteutetaan Firefox-selaimessa. Älä käytä valmiita Javascript-kirjastoja.
- Avaa avuksesi seuraavia sivuja:
Hello World
- Jos et ole vielä aktivoinut kotisivutilaasi sekä UNIX-käyttöä niin tee se nyt. Kts. Miten aktivoin henkilökohtaisen kotisivutilan?.
- Luo itsellesi W:-asemalle TIEA2120-kansio ja sen alle alikansio ohjaus1. Jos sinulla ei ole vielä W:-asemaa omalla koneellasi, niin lue Miten saan verkkolevyt käyttöön etänä Windowsilla?.
- Kopioi itsellesi tiedostot pohja.html ja ohjaus1.js. Tallenna nämä edellä tekemääsi ohjaus1-kansioon. Huom. Älä tallenna Firefoxin File-valikon kautta vaan valitsemalla linkin kohdalla hiiren kontekstivalikosta Save Link As.
- Avaa Firefox-selain ja lataa sillä osoite http://users.jyu.fi/~omatunnus/TIEA2120/ohjaus1/pohja.html. Huomaa isot ja pienet kirjaimet ja korvaa omatunnus omalla käyttäjätunnuksellasi.
- Avaa Firefoxin Javascript-console SHIFT+CTRL+K-näppäinyhdistelmällä tai Tools|Web developer|Web Console-menuvalinnalla.
- Jos kaikki meni oikein niin konsolissa pitäisi lukea testi.
- Muuta ohjaus1.js-tiedostosta console.log-rivi seuraavanlaiseksi:
console.log("Hello world!");Käytä editorina Visual Studio Codea tai Notepad++ tai jotain muuta koodieditoria.
Visual Studio Coden liveserver-ominaisuudesta voi olla paljon apua.
Muista tallentaa muutettu tiedosto
-
Asenna Visual Studio Codeen myös ESlint-laajennus
Lisäksi voit purkaa projektisi kansioon eslint.zip-paketin, jolloin vältyt npm:n asentamiselta. Viikkotehtäväpohjissa on erillinen selaimessa toimiva jshint, jonka virheilmoitukset näkyvät selaimen konsolissa.
JSHint ja ESLint ovat työkaluja, jotka automaattisesti tarkistavat javascript-koodin laatua ja virheitä. ESLintin tai JSHintin voi asentaa laajennoksena Visual Studio codeen.
- Lataa sivu uudelleen (CTRL+R). Konsolissa näkyvän tekstin pitäisi nyt olla Hello world!.
-
lisää seuraava rivi, jos haluat visual studio coden ajavan TypeScriptin tyyppitarkistuksen
ohjelmakoodillesi. Koodisi ei tarvitse olla TypeScriptiä.
Mahdolliset ongelmat näet Visual Studio Coden problems-ikkunassa View|Problems (Ctrl+Shift+M)
Saatat saada myös jotain turhia virheilmoituksia
//@ts-check
Muuttujat
- Lisää ohjaus1.js-tiedostoon muuttujien esittelyjä:
let kissa = "Mirri"; let koira = "Musti"; - Lisää myös sopivat console.log-rivit joilla tulosta muuttujien arvot konsoliin:
Tallenna ja lataa sivusi uudelleen selaimessa ja tarkastele mitä konsoliin ilmestyi.console.log(kissa); console.log(koira); - Muuta kissan nimeä:
kissa = "Katti"; - Kokeile ladata sivu uudelleen. Kirjoita konsolin alalaidalla olevalle riville kissa ja paina enter. Voit suoraan konsolin avulla tutkia ympäristössä voimassa olevia muuttujia ja niiden sisältöä. Kokeile kirjoittaa riville koira.
Debuggaus
- Vaihda Firefoxin web-työkaluissa Debugger-välilehdelle.
- Valitse Sources-listauksesta ohjaus1.js niin saat oman ohjelmakoodi näkyville
- klikkaa hiirellä riviä jossa lukee console.log(koira). Rivin numero muuttuu siniseksi. Olet nyt lisännyt riville breakpointin. Breakpointin saat pois jos kilkkaat riviä uudelleen. Hiiren oikealla painikkeella saat lisää mahdollisia valintoja.
- Lataa sivusi uudelleen. Ohjelman suorituksen pitäisi nyt keskeytyä breakpointin kohdalle
- Kokeile console-välilehdellä kirjoittaa kissa ja katso mikä on kissamuuttujan arvo. Sen pitäisi olla nyt "Mirri", koska viimeistä koodiriviä ei ole vielä suoritettu.
- Palaa takaisin Debugger-välilehdelle. Lisää oikean laidan Add watch expression kohtaan kissa. Nyt sinun pitäisi nähdä mikä on kissamuuttujan arvo.
- Debugger-välilehden vasemmasta laidasta löytyvät painikkeet joilla voit kontrolloida ohjelman suoritusta
.
- F8 Resume
- F10 Step Over
- F11 Step Into
- SHIFT+F11 Step Out
- Kokeile F10-painikkeella suorittaa ohjelmasi askel kerrallaan niin monesti, että ohjelmasi suoriutuu loppuun asti.
Tietorakenteet
- Luo uusi taulukko
(Array)
let elukat = ['Kissa','Koira', 'Hiiri', 'Koira']; - Luo myös Set-tyyppinen
tietorakenne ja alusta se elukat-taulukon sisällöllä:
const elaimet = new Set(elukat);const tekee muuttujasta vakion, jota ei voida muuttaa.
- Taulukko (Array) toimii samaan tapaan kuin C#- tai Java-kielissä. Yritä lisätä ohjelmaasi seuraavat toiminnot.
Käytä apunasi
Array-objektin
avustusta.
- Tulosta elukat-taulukon ensimmäinen alkio
- Tulosta taulukon pituus
- Lisää
taulukon loppuun 'Kettu'
Array(5) [ "Kissa", "Koira", "Hiiri", "Koira", "Kettu" ] - Lisää
taulukon alkuun 'Rotta'
Array(6) [ "Rotta", "Kissa", "Koira", "Hiiri", "Koira", "Kettu" ] - Poista
taulukon kolmas alkio
Array(5) [ "Rotta", "Kissa", "Hiiri", "Koira", "Kettu" ] - Poista
taulukon viimeinen alkio
Array(4) [ "Rotta", "Kissa", "Hiiri", "Koira" ] - Poista
taulukon ensimmäinen alkio
Array(3) [ "Kissa", "Hiiri", "Koira" ] - Jos edellä teit poistot oikein, niin sinulta hävisi taulukosta toinen koira. Lisää taulukkoon paikkaan kaksi "Koira". Joudut käyttämään splice-metodia.
Array(4) [ "Kissa", "Hiiri", "Koira", "Koira" ]
let maarat = new Map();
maarat.set("Kissa", 2);
maarat.set("Koira", "1");
maarat.set("Hiiri", 0);
let maarat2 = new Map([["Kissa", 2],["Koira", "1"],["Hiiri", 0]])
console.log(JSON.parse(JSON.stringify(obj)))
Silmukat
- Tulosta jokainen elukat-taulukon alkio konsoliin omalle rivilleen. Tarvitset for-silmukan:
for(let i=0; i<elukat.length; i++) { console.log( elukat[i] ); }let määrittää lohkon sisäisen muuttujan. var-sanalla esitellyn muuttujan näkyvyysalue on aina koko funktio.
- Kokeile tehdä sama tulostus käyttäen for...of-silmukkaan.
- Kokeile tehdä sama vielä for...in-silmukalla. Taulukoiden yhteydessä ei kannata käyttää for...in-silmukkaa, jos läpikäyntijärjestyksellä on merkitystä.
- Tulosta myös maarat-tietorakenteen sisältö siten, että jokainen avain-arvo-pari tulee omalle rivilleen. Käytä
for...of-silmukkaa. Tapoja on useita
erilaisia:
for (let [key, value] of maarat) { console.log(key + ' = ' + value); } // tulostaa pelkät avaimet for (let key of maarat.keys()) { console.log(key); } // tulostaa pelkät arvot for (let value of maarat.values()) { console.log(value); } // sama kuin ensimmäinen for (let [key, value] of maarat.entries()) { console.log(key + ' = ' + value); } - Yksittäisen Map-tietorakenteen arvon saa get-funktiolla:
Kirjoita silmukka jossa käyt läpi kaikki elukat-tietorakenteen nimet ja kysyt maarat-rakenteelta montako kutakin elukkaa on. Käytät siis avaimena elukat-tietorakenteesta löytyviä eläinten nimiä.maarat.get("Kissa");
Merkkijonoja voi helposti yhdistellä +-operaattorillafor(let i=0; i<elukat.length; i++) { let elukka = elukat[i]; console.log( elukka + " : " + maarat.get( elukka ) ); } - Lisää elukoihin "Hevonen". Kokeile mitä edellä tehty silmukka nyt tekee.
If-lause
- Muuta viimeiseksi tekemääsi silmukkaa siten, että lasket samassa silmukassa kuinka paljon eläimiä on yhteensä.
Tulosta tämä tieto myös konsoliin.
let summa = 0; for(let i=0; i<elukat.length; i++) { let elukka = elukat[i]; console.log( elukka + " : " + maarat.get( elukka ) ); summa = summa + maarat.get( elukka ); } console.log(summa); - Mitä konsoliin tulostuu? Miksi?
- Laskutoimitus menee pilalle siinä vaiheessa, kun summattavaksi luvuksi tulee undefined ('Hevonen'). Sinun pitää
lisätä laskutoimitusta ennen
ehto, joka tarkistaa onko käytettävissä oikea numero. Kts. Number.parseInt.
let summa = 0; for(let i=0; i<elukat.length; i++) { let elukka = elukat[i]; console.log( elukka + " : " + maarat.get( elukka ) ); let maara = maarat.get(elukka); // Entäs jos maara on 0? if (Number.parseInt(maara)) { summa += Number.parseInt(maara); // muista Number.parseInt! } } - Laskutoimituksen tulos on edelleen väärin, koska koira esiintyy taulukossa useamman kerran. Kirjoita uusi versio edellisestä silmukasta ja käytä elaimet-tietorakennetta. Set-tyyppinen tietorakenne läpikäydään for..of-silmukalla.
- Muuta edellistä if-lausetta siten, että ehto on tosi vaikka numero olisikin 0. Tämä ei nyt vaikuta laskutoimitukseen mitenkään mutta voi jossain muussa ohjelmassa olla olennaista. Tarvitset isNaN-funktiota, koska Number.parseInt palauttaa NaN, jos merkkijonoa ei saada muunnettua numeroksi.
- Mitä tapahtuu jos lisäät hevoselle määräksi "2k"?
maarat.set("Hevonen", "2k"); - Number.parseInt-funktio parsii merkkijonosta numeroa niin pitkälle kuin pystyy. Tässä tapauksessa tulokseksi pitäisi jäädä numero 2. Miten korjaisit if-lausetta, että määräksi ei kelpaa numeroiden ja kirjainten sekamelska? Vinkki: isNaN.
- Muuta if-lause muotoon jossa määräksi kelpaavat vain numeroina syötetyt arvot eli mikään merkkijonosyöte ei kelpaa. Kannattaa kokeilla suoraan Number()-funktiota. Kts. isInteger(), Number() tai typeof.
Funktiot
- Kirjoita funktio, joka ottaa ensimmäisenä parametrina listauksen eläimistä ja toisena parametrina Mapin jossa
kerrotaan eläinten lukumäärät.
Funktio palauttaa eläinten yhteislukumäärän. Huom. Älä tulosta funktiossa mitään!
function summaa(elaimet, maarat) { let summa = 0; for(let elukka of elaimet) { let maara = maarat.get(elukka); if (Number.parseInt(maara)) { summa += Number.parseInt(maara); } } return summa; } - Kutsu edellä tekemääsi funktiota ja tulosta konsoliin saamasi arvo
Objektit
let elukka = new Object();
elukka.tyyppi = 'Kissa';
elukka.nimi = 'Mirri';
elukka.paino = 4;
// objektin ominaisuuksiin voi viitata kuin taulukon alkioihin:
console.log ( elukka['tyyppi'] );
console.log ( elukka['nimi'] );
elukka['paino'] = 5;
// toinen tapa luoda ja alustaa objekti. Vrt json
let elukka2 = {
tyyppi: 'Koira',
nimi: 'Musti',
paino: 10
};
Objektin sisältämät avaimet ja arvot voi käydä läpi for...in-silmukalla.
Luo omaan ohjelmaasi edellä tehdyt objektit ja kokeile tulostaa niiden sisältö konsoliin silmukalla.
Voit vapaasti keksiä ja lisätä uusia avaimia (attribuutteja) mihin tahansa javascript-objektiin.
Taulukot ja objektit ovat viitteitä
Javascriptissa objektit ovat viitteitä (osoittimia). Taulukko on vain erikoistapaus objektista. Taulukoiden ja objektien kopioiminen ei ole aivan yksinkertaista. Pelkkä sijoitusoperaatio kopioi vain osoittimen.
// kopioidaan elukat-taulukon osoitin uuteen muuttujaan
let otukset = elukat;
otukset[0] = 'Rotta';
// kokeile nyt tulostaa sekä elukat että otukset taulukot
jos halutaan kopioida koko taulukko se on tehtävä Array.from-funktiolla. Tee oikea kopio elukat-taulukasta.
Array.from tekee taulukosta vain pnnaillisen kopion eli shallow copyn eli jos taulukko sisältää osoittimia tai objekteja niin kopioidaan vain näiden osoittimet eikä oikeaa sisältöä.
let ihmiset = ["Tommi", "Ville", "Kalle"];
let elaimet = ["Kissa", "Koira", "Hevonen"]
let kaikki = [ihmiset, elaimet, "Testi", "testi"];
// tee kopio kaikki-taulukosta Array.from-funktiolla.
// Kokeile muuttaa ihmiset- ja elaimet-taulukoiden sisältöä.
// esim. elaimet[0] = "Kakadu";
// Miten kaikki-taulukosta tehdyn kopion sisältö muuttuu? Tutki debuggerilla
// Miten korjaisit asian eli estäisit kopion sisällön muuttumisen?
// Katso korjausehdotus vasta kun olet itse miettinyt ja kokeillut
Korjausehdotus
jos tehdään syvä kopio (deepcopy) eli halutaan kopioida kaikki kyseiseen taulukkoon liittyvät objektit kaikilta tasoilta, niin täytyy vain kopioida yksitellen jokainen osa.
let deepcopy = Array.from(kaikki);
deepcopy[0] = Array.from(kaikki[0]);
deepcopy[1] = Array.from(kaikki[1]);
edellä oleva on sama kuin seuraava:
let deepcopy = Array.from(kaikki);
deepcopy[0] = Array.from(ihmiset);
deepcopy[1] = Array.from(elaimet);
Jos rakenne sisältää sisäkkäisiä taulukoita ja/tai objekteja, on
jokainen osa kopioitava erikseen. Mahdollisesti tasoja voi olla useita.
Tässä on vain kaksi tasoa.
Monimutkaisemmat tietorakenteet
Usein joudutaan käsittelemään sisäkkäisiä tietorakenteita. Taulukoita, joiden sisällä on lisää taulukoita tai objekteja, tai objekteja, jotka sisältävät uusia objekteja ja taulukoita.
Kts. Javascript-tietorakenteet
Elaintarha on objekti, joka sisältää ominaisuudet tyypit ja elaimet. Tyypit on taulukko. Elaimet on myös taulukko. Tyypit sisältää merkkijonoja. Elaimet sisältää objekteja. Lisää seuraava tietorakenne omaan ohjelmaasi ja tulosta se console.log- tai console.dir-komennolla konsoliin. Tämän jälkeen voit konsolissa selailla tarkemmin rakennetta.
let elaintarha = {
"tyypit": [
"Kissa",
"Koira",
"Hiiri"
],
"elaimet": [
{
"tyyppi": "Kissa",
"nimi": "Mirri",
"paino": 5
},
{
"tyyppi": "Koira",
"nimi": "Musti",
"paino": 10
},
{
"tyyppi": "Koira",
"nimi": "Murre",
"paino": 10
}
]
};
- Kirjoita funktio, joka tulostaa konsoliin kaikki tyypit. Tuo funktiolle parametrina elaintarha.
function tyypit(e) { // riittää kun otetaan tyypit-taulukko käsittelyyn let tyypit = e.tyypit; for(let tyyppi of tyypit) { console.log(tyyppi); } } - Kirjoita funktio, joka tulostaa konsoliin kaikkien eläinten nimet
- Kirjoita funktio, joka tulostaa riveittäin tyypit ja tiedon montako eläintä tätä tyyppiä tarhasta löytyy
- Miten saisit tallennettua kunkin tyypin yhteyteen sen määrän? Koita muuttaa tietorakenteen määrittelyä siten, että tyypit ovatkin objekteja ja lisää niille ominaisuudeksi lkm. Miten edellä tekemääsi tulostusfunktiota täytyy nyt muuttaa?
import ja export
- Haluatko jakaa koodisi useampaan tiedostoon? Haluatko kirjoittaa yleiskäyttöisen moduulin? Kts.Export and Import ja JavaScript modules
- Muuta script-elementin tyypiksi module:
Tähän on myös asetettava asynkroninen lataaminen päälle. Ilman tätä moduulityyppinen skripti ei toimi xhtml-dokumenteilla esim. Google Chromessa.<script async="" type="module" src="import.js"></script> - Kirjoita moduulissa (importoitavassa) js-tiedostossa (summa.js) export kaikkien
niiden funktioiden ja muuttujien eteen joiden haluat olla importoitavissa
// kts. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters export function summa(...args) { let summa = 0; for(let arg of args) { summa = summa + arg; // hajoaa, jos parametrit eivät olekkaan numeroita } return summa; } - import-komennolla saat nyt summa.js-tiedostossa olevan
funktion käyttöösi seuraavasti:
import {summa} from './summa.js'; - Kokeile toteuttaa javascript-sovellus, joka käyttää edellä kirjoitettua summa-funktiota erillisestä js-tiedostosta. Laske paljonko on lukujen 1,2,4,8 ja 16 summa.
- Moduuliksi muutettua skriptiä ei voi testata lokaalisti. Sovellus on ladattava www-palvelimen kautta. Sivun osoite ei siis voi alkaa file://
- malliratkaisu
Käyttäjien kommentit