CSCI 4513
Week 8, Lecture 12
The exam is printed and handwritten — no computer.
A narrative about building a web app with 32 numbered blanks. Write the correct term, keyword, or CSS property on each line.
10 terms on the left, 10 definitions on the right. Write the matching letter in the box. Each definition used exactly once.
Describe a web app and explain how you'd apply at least 8 of the 10 course concepts. Graded on reasoning quality, not just naming concepts.
Odin's self-sacrifice reveals true mastery, just as the midterm tests what you've internalized under pressure without external support.
The Tale:
Odin, despite being the Allfather, sought even deeper knowledge — understanding of the runes, the fundamental symbols that held power over reality itself. He pierced himself with his own spear, Gungnir, and hung from Yggdrasil for nine days and nine nights, wounded and without food or water. On the ninth night, the runes revealed themselves to him. With a final cry, Odin grasped them and fell — transformed. He could now heal, curse, wake the dead, protect in battle, and shape fate itself.
<form action="/api" method="POST">
<fieldset>
<legend>Contact Info</legend>
<label for="email">Email:</label>
<input type="email"
id="email"
name="email"
required>
</fieldset>
<button type="submit">Send</button>
</form>
/* Red border when invalid */
input:invalid {
border: 2px solid red;
}
/* Green border when valid */
input:valid {
border: 2px solid green;
}
/* Blue glow on focus */
input:focus {
border: 2px solid blue;
box-shadow: 0 0 5px
rgba(0,100,255,0.5);
}
/* Named layout areas */
.container {
display: grid;
grid-template-columns: 280px 1fr;
grid-template-rows: 80px 1fr;
grid-template-areas:
"sidebar header"
"sidebar main";
gap: 20px;
}
.sidebar { grid-area: sidebar; }
.header { grid-area: header; }
/* Responsive card grid — no media queries needed */
.cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1rem;
}
auto-fit collapses empty tracks (items expand). auto-fill keeps empty tracks (items stay minimum size).
newthisnullnewget prop(), no parenthesesfunction Book(title, author) {
this.title = title;
this.author = author;
}
// Methods go on the prototype — shared, not copied
Book.prototype.info = function() {
return `${this.title} by ${this.author}`;
};
const b1 = new Book('Prose Edda', 'Snorri');
const b2 = new Book('Beowulf', 'Unknown');
// Both share the same info method via prototype chain
console.log(b1.info()); // "Prose Edda by Snorri"
new breaks everything — this points to the wrong thing.
function createLibrary() {
const books = []; // Private — lives in the closure
return {
addBook(title) {
books.push(title); // Accesses private array
},
count() {
return books.length; // Still has access!
}
};
}
const lib = createLibrary();
lib.addBook('Prose Edda');
console.log(lib.count()); // 1
console.log(lib.books); // undefined — private!
new needed. Private variables through closure, not prototype.
class Book {
constructor(title, author) {
this.title = title;
this.author = author;
}
get info() {
return `${this.title} by ${this.author}`;
}
}
class RareBook extends Book {
constructor(title, author, year) {
super(title, author); // Must call super() first!
this.year = year;
}
}
const r = new RareBook('Codex Regius', 'Unknown', 1270);
console.log(r.info); // Getter — no parentheses!
// Named exports
export function add(a, b) {
return a + b;
}
export const PI = 3.14;
// Default export (one per file)
export default class Game { }
// Named imports
import { add, PI } from './math.js';
// Default import
import Game from './game.js';
// Rename on import
import { add as sum } from './math.js';
import chains and emits one file the browser can load.
const library = [
{ title: 'Prose Edda', author: 'Snorri' },
{ title: 'Beowulf', author: 'Unknown' }
];
// Save to localStorage
localStorage.setItem('lib', JSON.stringify(library));
// Restore on next page load
const saved = localStorage.getItem('lib');
const restored = JSON.parse(saved);
console.log(restored[0].title); // "Prose Edda"
undefined, no circular references. Only plain data.
// capitalize.js
function capitalize(str) {
return str[0].toUpperCase() + str.slice(1);
}
module.exports = { capitalize };
// capitalize.test.js
const { capitalize } = require('./capitalize');
describe('capitalize', () => {
test('capitalizes first letter', () => {
expect(capitalize('hello')).toBe('Hello');
});
test('leaves rest unchanged', () => {
expect(capitalize('hELLO')).toBe('HELLO');
});
});
function Dog(name) {
this.name = name;
}
Dog.prototype.bark = function() {
return this.name + ' says woof!';
};
const dog1 = new Dog('Rex');
const dog2 = new Dog('Max');
dog1.bark = function() { return 'LOUD BARK!'; };
console.log(dog1.bark());
console.log(dog2.bark());
Answer: "LOUD BARK!" and "Max says woof!"
dog1 has its own bark method that shadows the prototype. dog2 has no own method, so it climbs the prototype chain and finds Dog.prototype.bark.
function createCounter() {
let count = 0;
return {
increment() { count++; },
getCount() { return count; }
};
}
const c1 = createCounter();
const c2 = createCounter();
c1.increment();
c1.increment();
c2.increment();
console.log(c1.getCount());
console.log(c2.getCount());
Answer: 2 and 1
Each call to createCounter() creates a new closure with its own count variable. They never share state.
function run() {
for (var i = 0; i < 3; i++) {
// do something
}
console.log(i); // (A) What prints?
}
function run2() {
for (let j = 0; j < 3; j++) {
// do something
}
console.log(j); // (B) What happens?
}
run();
run2();
(A) Prints 3 — var is function-scoped, leaks out of the for block.
(B) Throws a ReferenceError — let is block-scoped and doesn't exist outside the loop.
const settings = { theme: 'dark', fontSize: 16 };
// Save to localStorage
localStorage.setItem('prefs',
JSON.___(settings) // (1) Which method?
);
// On next visit — restore
const raw = localStorage.getItem('prefs');
const prefs = JSON.___(raw); // (2) Which method?
console.log(prefs.theme); // "dark"
(1) JSON.stringify(settings) — converts the object to a JSON string for storage.
(2) JSON.parse(raw) — converts the stored string back into a live JavaScript object.
Try matching these before clicking to reveal!
Terms
fr unitDefinitions
new)1 → E 2 → D 3 → C 4 → B 5 → A
On the real exam you'll describe a web app and explain how you'd apply 8+ concepts. Let's practice reasoning through one now.
Concept: CSS Grid Layout
Your app: A recipe-sharing website
Draft your answer: What does Grid do in your app, and why is it the right tool?
What: The overall shell of the page uses grid-template-areas to name the sidebar, header, and recipe-card regions. The recipe card grid uses repeat(auto-fit, minmax(250px, 1fr)) so cards reflow automatically on smaller screens.
Why: Grid is a two-dimensional layout system, making it the right tool for a page where I need to control placement along both axes simultaneously. Flexbox couldn't keep the card rows aligned as the grid.
var/let/const scopingJSON.stringify() and JSON.parse() each do and why📅 Next Class: Midterm Exam — Tuesday, March 10
📚 Coverage: Lectures 1–11 (no Async/APIs)
💪 You've got this!