Eksamen: REA3049-JS | Semester: Vår 2024 | Tema: OOP, pseudokode, Fibonacci, kalkulator, deepfakes, tidsbruk-datasett, Game of Life
En klasse definerer hvilke egenskaper og metoder objektene skal ha — selve objektet er en instans. I JavaScript er dette spesielt synlig: klasser er syntaktisk sukker for prototypemønsteret, der alle instanser deler samme prototype-objekt.
Når B arver fra A, er B en mer spesialisert versjon av A. Generaliseringen går motsatt vei: A er generaliseringen.
| a | DISPLAY c | b etter DECREMENT | c etter c+b |
|---|---|---|---|
| 2 | 5 | 3 | 5+3 = 8 |
| 3 | 8 | 2 | 8+2 = 10 |
| 4 | 10 | 1 | 10+1 = 11 |
SET num to -2
FOR hver num LESSER THAN OR EQUAL TO 2
IF num LESSER THAN 0
IF num % 2 EQUAL TO 0
DISPLAY num + " er negativt og partall"
ELSE
DISPLAY num + " er negativt og oddetall"
ENDIF
ELSE IF num EQUAL TO 0
DISPLAY num + " er null"
ELSE
IF num % 2 EQUAL TO 0
DISPLAY num + " er positivt og partall"
ELSE
DISPLAY num + " er positivt og oddetall"
ENDIF
ENDIF
ENDFOR
Fibonacci-sekvensen: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55. a[10] = 55.
Algoritmen genererer Fibonacci-tallene iterativt: hvert nytt tall er summen av de to foregående, med startverdiene 0 og 1. Iterativ tabulering gir O(n) tid og O(n) plass — mye raskere enn naiv rekursjon som er eksponentiell.
// oppgave5c.mjs — første ti Fibonacci-partall
export function fibonacciPartall(antall = 10) {
let a = 0n, b = 1n; // BigInt for å unngå overflyt på store tall
const partall = [];
while (partall.length < antall) {
if (a % 2n === 0n) partall.push(a);
[a, b] = [b, a + b];
}
return partall;
}
console.log(fibonacciPartall().map(String));
// ["0","2","8","34","144","610","2584","10946","46368","196418"]
partall.length < 10? → JA: er a % 2n == 0n? → JA: legg a i partall → uansett: [a, b] ← [b, a+b] → tilbake til LOOP. NEI: SKRIV partall → Slutt.
// kalkulator.mjs
export class KalkulatorFeil extends Error {
constructor(melding) {
super(melding);
this.name = "KalkulatorFeil";
}
}
export class Kalkulator {
pluss(a, b) { return a + b; }
minus(a, b) { return a - b; }
gange(a, b) { return a * b; }
dele(a, b) {
if (b === 0) throw new KalkulatorFeil("Kan ikke dele på null.");
return a / b;
}
}
// Test-program
function testKalkulator() {
const k = new Kalkulator();
console.assert(k.pluss(2, 3) === 5);
console.assert(k.minus(10, 4) === 6);
console.assert(k.gange(3, 7) === 21);
console.assert(k.dele(20, 4) === 5);
// Feil 1: Deling på null
try { k.dele(10, 0); }
catch (e) { console.log(`OK – fanget: ${e.message}`); }
// Feil 2: Strengkonkatenering når man forventet tall
// JS er løs typet, så pluss("2", 3) returnerer "23" — ikke en feil i seg selv,
// men en kilde til subtile bugs. Bedre: verifiser typer.
const resultat = k.pluss("2", 3);
console.log(`Type-pitfall: pluss("2", 3) = ${resultat} (string!)`);
// Feil 3: NaN propageres
const ugyldig = k.pluss(NaN, 5);
console.assert(Number.isNaN(ugyldig));
console.log("Tester ferdig.");
}
testKalkulator();
Identifiserte feil/unntak: (1) Deling på null — håndtert med KalkulatorFeil. (2) Type-coercion — JavaScripts +-operator gjør strengkonkatenering hvis ett operand er streng. Forsvarsstrategi: valider med typeof a === "number" ved input. (3) NaN-propagering — én NaN-verdi forurenser hele kjeden. Bør sjekkes med Number.isFinite().
| Spm. | Riktig svar | Begrunnelse |
|---|---|---|
| a) Hva er deepfakes? | manipulerte medier | Deepfakes er video/lyd/bilde manipulert med KI/maskinlæring. |
| b) Mulig konsekvens? | økt risiko for politisk manipulering | Som artikkelen viser — deepfakes brukes for å sverte politiske motstandere. |
| c) Hvordan avsløre? | ved å bruke teknikker fra kildekritikk | Sjekke avsender, søke etter videoen andre steder, vurdere sannsynlighet. |
Hvordan avsløre: Lytte etter unaturlige pauser, robotiske intonasjoner og lyd som ikke matcher leppene. På video — se etter ujevn belysning, kanter mellom ansikt og bakgrunn som flimrer, unaturlige blunkemønstre. Ved kritiske beslutninger — særlig økonomiske transaksjoner — alltid verifisere gjennom en annen kanal.
Forebyggende tiltak i bedriften:
For: Realistiske press-scenarier kan avsløre faglig modenhet bedre enn et tradisjonelt intervju. Ved åpenhet og samtykke kan det være et legitimt verktøy for stillinger der krisemestring er kritisk.
Mot:
Konklusjon: Deepfakes i ansettelsesprosesser kan ha legitime formål, men forutsetter eksplisitt samtykke, klare retningslinjer for sletting av biometriske data, og grundig etisk evaluering før innføring.
// oppgave9.mjs — analyse av SSB-tidsbruk-datasett (2000)
import { readFileSync, writeFileSync } from "node:fs";
import readline from "node:readline/promises";
import { stdin as input, stdout as output } from "node:process";
function tidTilMinutter(verdi) {
if (!verdi) return 0;
const [t, m = "0"] = verdi.split(".");
return Number(t) * 60 + Number(m);
}
function lesData(filsti = "tidsbruk_2000.csv") {
const linjer = readFileSync(filsti, "utf-8").trim().split(/\r?\n/);
const [headerLinje, ...rest] = linjer;
const headers = headerLinje.split(";");
return rest.map(linje => {
const verdier = linje.split(";");
return Object.fromEntries(headers.map((h, i) => [h, verdier[i]]));
});
}
function visTabell(data, kjonn = "Alle") {
const rader = kjonn === "Alle" ? data : data.filter(r => r["Kjønn"] === kjonn);
console.log(`\n${"Aktivitet".padEnd(28)}${"Kjønn".padEnd(10)}${"Tidsbruk".padStart(10)}`);
console.log("-".repeat(48));
for (const r of rader) {
console.log(r["Aktivitet"].padEnd(28) + r["Kjønn"].padEnd(10) + r["Tidsbruk"].padStart(10));
}
}
// SVG-stolpediagram (uten eksterne bibliotek)
function stolpediagramSvg(data, kjonn) {
const rader = data.filter(r => r["Kjønn"] === kjonn && (r["Type"] || "aktivitet") === "aktivitet");
const bredde = 800, hoyde = 500, padding = 80;
const stolpebredde = (bredde - padding * 2) / rader.length - 4;
const verdier = rader.map(r => tidTilMinutter(r["Tidsbruk"]));
const maks = Math.max(...verdier);
let svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${bredde}" height="${hoyde}">`;
svg += `<text x="${bredde/2}" y="30" text-anchor="middle" font-size="18" font-weight="bold">Tidsbruk per aktivitet — ${kjonn}</text>`;
rader.forEach((r, i) => {
const v = verdier[i];
const h = ((hoyde - padding * 2) * v) / maks;
const x = padding + i * (stolpebredde + 4);
const y = hoyde - padding - h;
svg += `<rect x="${x}" y="${y}" width="${stolpebredde}" height="${h}" fill="#1976D2"/>`;
});
svg += `</svg>`;
writeFileSync(`stolpe_${kjonn.toLowerCase()}.svg`, svg);
console.log(`Stolpediagram lagret som stolpe_${kjonn.toLowerCase()}.svg`);
}
async function main() {
const data = lesData();
const rl = readline.createInterface({ input, output });
const kjonn = ((await rl.question("Velg kjønn (Alle / Menn / Kvinner): ")).trim());
rl.close();
const normalisert = kjonn[0].toUpperCase() + kjonn.slice(1).toLowerCase();
visTabell(data, normalisert);
if (normalisert !== "Alle") stolpediagramSvg(data, normalisert);
}
main();
<!DOCTYPE html>
<html lang="no">
<head><meta charset="utf-8"><title>Game of Life</title></head>
<body style="text-align:center;font-family:sans-serif">
<h2>Conways Game of Life</h2>
<canvas id="lerret" width="540" height="540" style="border:1px solid #999"></canvas>
<div>
<button id="toggle">Start/Pause</button>
<button id="steg">Steg</button>
<button id="tom">Tøm</button>
</div>
<script>
const RUTER = 30, CELLE = 18;
const START_LEVENDE = 1 / 3;
class Spillebrett {
constructor(rader, kolonner) {
this.rader = rader;
this.kolonner = kolonner;
this.celler = Array.from({length: rader},
() => Array.from({length: kolonner}, () => Math.random() < START_LEVENDE));
}
tom() {
for (let r = 0; r < this.rader; r++)
for (let k = 0; k < this.kolonner; k++) this.celler[r][k] = false;
}
veksle(r, k) { this.celler[r][k] = !this.celler[r][k]; }
levendeNaboer(r, k) {
let n = 0;
for (let dr = -1; dr <= 1; dr++)
for (let dk = -1; dk <= 1; dk++) {
if (dr === 0 && dk === 0) continue;
const nr = r + dr, nk = k + dk;
if (nr >= 0 && nr < this.rader && nk >= 0 && nk < this.kolonner && this.celler[nr][nk]) n++;
}
return n;
}
nesteGenerasjon() {
const ny = this.celler.map(rad => [...rad]);
for (let r = 0; r < this.rader; r++)
for (let k = 0; k < this.kolonner; k++) {
const n = this.levendeNaboer(r, k);
ny[r][k] = this.celler[r][k] ? (n === 2 || n === 3) : (n === 3);
}
this.celler = ny;
}
}
const brett = new Spillebrett(RUTER, RUTER);
const ctx = document.getElementById("lerret").getContext("2d");
let kjorer = false;
function tegn() {
for (let r = 0; r < brett.rader; r++)
for (let k = 0; k < brett.kolonner; k++) {
ctx.fillStyle = brett.celler[r][k] ? "#1a2b4a" : "#fafafa";
ctx.fillRect(k * CELLE, r * CELLE, CELLE - 1, CELLE - 1);
}
}
document.getElementById("lerret").addEventListener("click", e => {
const r = (e.offsetY / CELLE) | 0;
const k = (e.offsetX / CELLE) | 0;
brett.veksle(r, k);
tegn();
});
document.getElementById("toggle").onclick = () => { kjorer = !kjorer; if (kjorer) loop(); };
document.getElementById("steg").onclick = () => { brett.nesteGenerasjon(); tegn(); };
document.getElementById("tom").onclick = () => { kjorer = false; brett.tom(); tegn(); };
function loop() {
if (!kjorer) return;
brett.nesteGenerasjon();
tegn();
setTimeout(loop, 200);
}
tegn();
</script></body></html>
Spillebrett-klassen håndterer regler og tilstand; tegningen skjer i en ren funksjon mot Canvas-konteksten.levende → naboer===2 || naboer===3 og død → naboer===3.123456/
├── oppgave5c/oppgave5c.mjs
├── oppgave6/kalkulator.mjs
├── oppgave9/
│ ├── oppgave9.mjs
│ └── tidsbruk_2000.csv
├── oppgave10/game_of_life.html
└── README.md (krev: Node.js 18+ for ESM)