CSCI 4513
Week 7, Lecture 14
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 and already possessing great wisdom from Mimir's Well, sought even deeper knowledge—understanding of the runes, the fundamental symbols that held power over reality itself. But this knowledge couldn't be given freely; it required the ultimate sacrifice.
Odin pierced himself with his own spear, Gungnir, and hung himself from Yggdrasil, the World Tree. For nine days and nine nights, he hung there without food or water, wounded and exposed to the elements. He existed in a state between life and death, suffering alone with no one to help or comfort him. On the ninth night, at the moment when he had given everything, the runes revealed themselves to him. With a final cry, Odin grasped the runes and fell from the tree, having gained knowledge that transformed him—he could now use the runes to heal, to curse, to wake the dead, to protect in battle, and to shape fate itself.
Click each card to reveal thoughts!
Odin already had significant power before hanging on Yggdrasil, yet he sought deeper knowledge. Why is testing important even when you think you understand something?
Consider: You don't know what you don't know. Tests reveal gaps in understanding that feel like knowledge. Under pressure, superficial understanding fails while deep knowledge holds. Testing distinguishes "I can follow this" from "I can create this."
The runes only revealed themselves after nine days of suffering. What is the relationship between struggle and true learning?
Consider: Comfort prevents transformation. Struggle forces you to truly engage with concepts, not just memorize them. The difficulty of learning is the mechanism of learning—your brain rewires through effort, not observation.
Odin hung alone without help. How does this isolation change what the test measures? What's the difference between understanding with resources versus alone?
Consider: Resources let you borrow knowledge; isolation reveals what you've truly internalized. In the real world, you have resources—but speed and confidence come from deep knowledge. Tests measure what you can do under pressure, which predicts real performance.
<form action="/submit" method="POST">
<label for="email">Email:</label>
<input type="email" id="email" required>
<label for="age">Age:</label>
<input type="number" id="age"
min="18" max="120">
</form>
requiredminlength, maxlengthmin, maxpattern (regex)type (email, number, date)const dialog = document.querySelector('dialog');
// Open as modal (with backdrop)
dialog.showModal();
// Open without modal backdrop
dialog.show();
// Close dialog
dialog.close();
// Style the backdrop
dialog::backdrop {
background: rgba(0, 0, 0, 0.5);
}
showModal() includes backdrop and prevents interaction with other elements!
.container {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: auto 1fr auto;
gap: 20px;
}
.item {
grid-column: span 2; /* Span 2 columns */
grid-row: 1 / 3; /* From line 1 to 3 */
}
/* Responsive grid */
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
function Player(name, level) {
this.name = name;
this.level = level;
}
// Add methods to prototype
Player.prototype.greet = function() {
return `Hello, I'm ${this.name}!`;
};
const player1 = new Player('Thor', 100);
console.log(player1.greet()); // "Hello, I'm Thor!"
new keyword! Methods go on prototype, not in constructor.
function createPlayer(name, level) {
let health = 100; // Private variable
return {
name, // Public property
level,
getHealth() { // Public method accessing private var
return health;
},
takeDamage(damage) {
health -= damage;
}
};
}
const player = createPlayer('Odin', 50);
console.log(player.health); // undefined (private!)
console.log(player.getHealth()); // 100
class Player {
#health = 100; // Private field
constructor(name, level) {
this.name = name;
this.level = level;
}
greet() {
return `I'm ${this.name}, level ${this.level}`;
}
get health() {
return this.#health;
}
static compare(p1, p2) {
return p1.level - p2.level;
}
}
class Warrior extends Player {
constructor(name, level, weapon) {
super(name, level);
this.weapon = weapon;
}
}
// Named exports
export function add(a, b) {
return a + b;
}
export const PI = 3.14;
// Default export
export default class Game {
// ...
}
// Named imports
import { add, PI } from './math.js';
// Default import
import Game from './game.js';
// Import all
import * as math from './math.js';
// Parse JSON string
const obj = JSON.parse('{"a":1}');
// Convert to JSON
const str = JSON.stringify(obj);
// Creating a promise
const promise = new Promise((resolve, reject) => {
if (success) {
resolve(data);
} else {
reject(error);
}
});
// Consuming a promise
promise
.then(data => console.log(data))
.catch(error => console.error(error))
.finally(() => console.log('Done'));
async function getData() {
try {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
return data;
} catch (error) {
console.error('Error:', error);
}
}
// Using the function
getData().then(data => console.log(data));
await only works inside async functions!
# Amend last commit
git commit --amend
# Interactive rebase (last 3 commits)
git rebase -i HEAD~3
# Reset (soft keeps changes staged)
git reset --soft HEAD~1
# Revert (safe, creates new commit)
git revert HEAD
# Force push (DANGEROUS!)
git push --force-with-lease
describe('Calculator', () => {
test('adds two numbers', () => {
expect(add(2, 3)).toBe(5);
});
test('throws on invalid input', () => {
expect(() => add('a', 2)).toThrow();
});
});
// Common matchers
expect(value).toBe(4);
expect(value).toEqual({a: 1});
expect(value).toBeTruthy();
expect(value).toContain('item');
expect(fn).toHaveBeenCalled();
// What will this output?
async function test() {
console.log('A');
await Promise.resolve();
console.log('B');
}
console.log('C');
test();
console.log('D');
Answer: C, A, D, B
Explanation: "C" logs first, then test() is called logging "A", then "D" logs (async functions don't block), then "B" logs after await resolves.
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!';
};
// What do these output?
console.log(dog1.bark());
console.log(dog2.bark());
Answer: "LOUD BARK!" and "Max says woof!"
Explanation: dog1 has its own bark method which overrides the prototype. dog2 still uses the prototype method.
function createCounter() {
let count = 0;
return {
increment() {
count++;
},
getCount() {
return count;
}
};
}
const counter1 = createCounter();
const counter2 = createCounter();
counter1.increment();
counter1.increment();
counter2.increment();
// What do these output?
console.log(counter1.getCount());
console.log(counter2.getCount());
Answer: 2 and 1
Explanation: Each factory function call creates a new closure with its own count variable. They don't share state.
đź“… Next Class: Midterm Exam
📚 Coverage: All material from Lectures 1-14
đź’Ş You've got this! The "runes" you've mastered will serve you well.