# Part 1 ## Advanced JS -- ### Goals - Learn more JS features & internals - Explore Functional and Object-Oriented JS - Have some fun with JS in the browser -- ### Ready? # Let's go!
## Review: JS & the DOM -- Getting HTML elements from JS ```js document document.getElementById('my-id') document.querySelectorAll('#my-id') document.documentElement ``` -- Attributes ```js element.getAttribute("name") element.setAttribute("name", "toggle") ``` -- Attribute presence vs. value ```js btn.getAttribute("disabled") // "" btn.setAttribute("disabled", "false") btn.hasAttribute("disabled") // true btn.deleteAttribute("disabled") ``` -- Event Handlers ```js element.addEventListener('click', (e) => { console.log('you clicked on:', e.target) })` ``` --
## Quick Win ### Dark Mode
-- ### `dark-mode/index.html` 1. Grab the `html` element 1. Read the `data-theme` attribute 1. Add an event handler to the `#toggle` checkbox 1. Change `data-theme` to `'light'` or `'dark'` --
## Time (and other problems)
'The Persistence of Memory' by Salvador Dali
-- ## Review: Async JS `async` and `await` ```js async function fetchData() { const response = await fetch("some.api/resource"); return await response.json(); } ``` -- ### JS, why are you like this? ![Meme with text "Y U NO JUST RUN CODE NOW"](../assets/y-u-no-run.png) -- ![History channel dude meme: "It's quite simple... but at the same time, quite complex"](../assets/simple.jpg) -- - Some actions take time to complete - The browser needs to stay responsive - But JS is
single-threaded
(does 1 thing at a time) Let's dig a little deeper to understand how it works! -- --
JS runtime
e.g. V8, SpiderMonkey
Browser
e.g. Chrome, Firefox
--
|
-- | --
Call Stack
| Functions stack up when called, run to completion, pop off when done
Heap
| Data is stored in a big ol' pile of memory
Queue
| Messages get in line to send function calls to the stack
Event Loop
| Infinite `while` pops 1st message from queue & sends call to stack (if empty)
|
-- ![Diagram of a call stack with frames on it, a heap with objects in it, and a queue with messages in it](../assets/internals.png) -- ![Lord of the Rings meme: 'One does not simply... completely explain complex topics with a single meme'](../assets/simply.jpg) --
'What the heck is the event loop anyway?' by Phillip Roberts
-- ## Browser APIs
-- ### Window where JS is running ```js console.log(window); // Window { document: document, location: Location{...}, ... } window.location.toString(); // https://developer.mozilla.org/en-US/docs/Web/API/Window document === window.document // true console === window.console // true ``` -- ### User's Browser ```js console.log(navigator); // Navigator { // permissions: Permissions, // mediaCapabilities: MediaCapabilities, // oscpu: "Intel Mac OS X 10.15", // ... // } console.log(navigator.userAgent); // "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15) // Gecko/20100101 Firefox/119.0" ``` -- ### Dates & times ```js const today = new Date(); console.log(today.toDateString()); // "Tue Nov 28 2023" console.log(today.toLocaleDateString()); // 11/28/2023 console.log(Date.now()) // 1701155454525 ``` -- ### One trillion ![Clip from Austin Powers zooming in on Dr. Evil raising pinky to mouth](../assets/dr-evil.gif) ### milliseconds -- ### Timing things ```js const start = Date.now(); longRunningOperation(); const end = Date.now(); const elapsed = end - start; console.log(`that took ${elapsed/1000}s`); ``` -- ### `console.time` ```js console.time('stopwatch'); runAround(); console.timeLog('stopwatch'); // stopwatch: 17293ms console.time('lap'); runAround(); console.timeEnd('lap'); // lap: 3850ms - timer ended runAround(); console.timeEnd('stopwatch'); stopwatch: 24711ms - timer ended ``` -- ### Timeouts ```js console.log('prints immediately'); setTimeout(() => console.log('prints >1 second later'), 1000); console.log('prints immediately?'); ``` -- ![Spongebob Squarepants still saying "Many hours later..."](../assets/later.jpg) -- ### Canceling timers ```js const countdown = setTimeout(() => { console.log('liftoff') }, 10000); console.log(countdown); // 123 (timeout id) abortButton.addEventListener('click', () => { clearTimeout(countdown) }); ``` -- ### Intervals ```js const traveling = setInterval(() => { console.log('Are we there yet?'); }, 1000); // arrive clearInterval(traveling) ``` -- ### JS jargon The
Event Loop
is how JS avoids
blocking
the browser's execution -- ![Hotline Bling meme with Drake disapproving of "Blocking (sync)" and approving of "Non-blocking (async)"](../assets/drake-approves.png)
## Quick Win ### Random Clock ![Black and white movie clip of women randomly dressed up as clocks and dancing](../assets/clocks.gif) -- ### `clock/index.html` 1. Get the `p` element with id `'time'` 1. Get the current time as a localized time string 1. Set the text of the `#time` element to the time string 1. Style the `p` with random values for `top`, `left`, and `color` 1. Repeat every 3 seconds -- ![Clip of Drake dancing in Hotline Bling video](../assets/hotline-bling.gif)
## Functions -- ### Back to callbacks... `addEventListener(eventName, handlerFn)` `setTimeout(callbackFn, delay)` `setInterval(callbackFn, interval)` It's actually cool that JS can do this! -- ### Programming jargon JS has
first-class functions
(functions can be passed around to functions like any other data) Functions that operate on other functions are
higher-order functions
-- ### Functions as return values ```js function makeLogger(loglevel) { return function log(msg) { console[loglevel](msg); } } let level = 'warn'; const worryUser = makeLogger(level); worryUser('ohno'); // Warning: ohno ``` -- ### But what if we... ```js level = 'error'; worryUser('terrible mistake'); // Warning: terrible mistake ``` -- ### Review: Scope ![Clip from Hey Arnold of a stretch limo arriving, caption "(horn honks)"](../assets/limo.gif) -- `worryUser` "remembers" what `level` was _when `worryUser` was declared_ --
-- ### Programming jargon A
closure
is a function that
closes
over values in its defining scope, remembering them forever -- We've entered the realm of
functional programming
![Clip from Jurassic Park of Samuel L Jackson's character saying "Hold on to your butts"](../assets/butts.gif)
## Programming paradigms -- Paradigm | Meta-Paradigm | Focus --- | --- | ---
FP
| Declarative | "what"
OOP
| Imperative | "how"
|
-- ### Object Oriented Programming (OOP) ```js const config = { paradigm: "imperative" }; const program = new Program(config); program.run = (args) => { console.log(`programming ${paradigm}ly`); } const output = program.run(); ``` -- ### Functional Programming (FP) ```js (function program({paradigm}) { return function run(args) { return `do stuff ${paradigm}ly` } })({paradigm: "declarative"})() ``` -- ### Sidebar: JS jargon
IIFE
: Immediately Invoked Function Expression ```js // Function expression function bling(line) { console.log('one thing'); } // Invoked function bling('hotline') // 'one thing' // IIFE (function bling(line) { console.log('one thing') })() ``` -- JS is called a
multi-paradigm
language
-- But under the hood... ![Clips from Scooby Doo where various villains are unmasked](../assets/meddling.gif) ...everything is Objects!
## Object-Oriented Programming
-- ### Review: `for ... of` ```js const engines = ['V8', 'SpiderMonkey']; for (let eng of engines) { console.log(eng.toUpperCase()); } // V8 // SPIDERMONKEY const doc = document.documentElement; for (let child of doc.children) { console.log(child.tagName); } // HEAD // BODY ``` -- ### Review: `.map()` ```js engines.map(e => e.toUpperCase); // Array [ "V8", "SPIDERMONKEY" ] doc.children.map(c => c.tagName); // TypeError: doc.children.map is not a function ``` -- ![Meme of confused woman with math formulas](../assets/math.gif) -- ```js typeof engines // "object" typeof doc.children // "object" ``` -- ![Clip from Parks & Recreation of Ben saying "Nothing you're saying is helpful"](../assets/helpful.gif) -- ### Every JS object has a _prototype_ ```js Object.getPrototypeOf(engines) // Array [] Object.getPrototypeOf(doc.children) // HTMLCollectionPrototype { ... } ``` -- ### Follow the prototype chain... ```js engines.__proto__ // Array [] engines.__proto__.__proto__ // Object {} engines.__proto__.__proto__.__proto__ // null ``` -- ![Food chain diagram with Zombies at the top](../assets/zombies.jpg) -- ### Recognize this? ```js engines.__proto__.__proto__.toString() // [object Object] ``` ![Tweet by @iamdevloper saying "If in doubt, and bored, set your username to '[object Object] and wait for the confused looks..."](../assets/iamdevloper.png) -- ### (Own) Properties ```js const obj = { name: "objectina", isGenie: true } Object.entries(obj); // Array [ ["name", "objectina"], ["isGenie", true] ] obj.toString // function toString() obj.hasOwnProperty('toString') // false obj.toString() // [object Object] ``` -- ### Resolving our `.map` confusion: ```js engines.hasOwnProperty('map') // false engines.__proto__.hasOwnProperty('map') // true ``` `Array` supplies the `.map` property `HTMLCollection` is not an array, doesn't have `.map` -- But how did `for ... of` work then? ![Meme of little girl looking confused](../assets/confused.gif)
## Iteration in JS -- ### Ways to iterate `.map()` only works on `Array` and its descendants but `for ... of` works on any
iterable
object -- ### What makes an object "iterable"? An iterable object has an
@@iterator
method, i.e.: - the object has a `[Symbol.iterator]` property - the value of that property is a function which returns an
iterator
object -- ### OK... so what makes an object an "iterator"? - Has a `.next()` method - `.next()` returns an object of shape
`{ done, value }` -- ### Generators - Special functions declared with `function*` - Can _pause_ execution with `yield` before a final stop with `return` - Qualify as both an `iterator` and an `iterable` - Make great values for `[Symbol.iterator]` -- ```js function* dogerator() { yield 'so iterator'; yield 'much generate'; yield 'wow'; return; } const meme = { [Symbol.iterator]: dogerator }; for (let phrase of meme) { console.log(phrase); } ```
## Quick Win ### Clockerator -- ### `clockerator/index.html` Build out the random clock to: 1. Implement a `getClock()` function that returns an iterable 1. The values of the iterable should be the current time, date, and day strings 1. Update the text of the `#time` element to the time string, `#date` to date, `#day` to day 1. Style each element with random values for `top`, `left`, and `color` 1. Repeat every 3 seconds
## Classy JS
-- ### `new` operator Creates a new _instance_ of a given object type, e.g. `new Date()` `new` calls a
constructor
function that creates an object of the given type -- ### Constructors Constructors use `this` to assign object properties ```js function Meme(img, text) { this.img = img; this.text = text; } const pooh = new Meme('regularPooh.jpg', 'using functions'); // { img: regularPoohImg, text: 'functions' } pooh.fancy // undefined ``` -- ### `class` Fancy syntax for declaring custom object types (classes) ```js class ClassyMeme { // property declarations fancy = true; img; text; // new instance constructor constructor(img, text) { this.img = img; this.text = text; } // method begPardon() { console.log('Do you have any Grey Poupon?') } } const ooh = new ClassyMeme('fancyPooh.jpg', 'using classes') ooh.fancy // true ooh.begPardon() ```
## Classy JS continued -- ### Declaring & instantiating ```js class Meme { constructor(img, text) { this.img = img; this.text = text; } } const fancyPooh = new Meme('fancyPooh.jpg', 'I am fancy'); ``` -- ### Methods ```js class Meme { constructor(img, text) { this.img = img; this.text = text; } lol() { console.log('lololol'); } } const fancyPooh = new Meme('fancyPooh.jpg', 'I am fancy'); fancyPooh.lol() // lololol ``` -- ### Override methods ```js class Meme { constructor(img, text) { this.img = img; this.text = text; } toString() { return `Meme: '${this.text}'` } } ``` ```js fancyPooh.toString() // Meme: 'I am fancy' ``` -- ### Properties, Getters & Setters ```js class Meme { constructor(img, text) { this.img = img; this.text = text; } get imgType() { const [name, ext] = this.img.split('.'); return ext; } set imgType(newExt) { const [name] = this.img.split('.'); this.img = [name, newExt].join("."); } } ``` -- ```js fancyPooh.img // 'fancyPooh.jpg' fancyPooh.imgType // 'jpg' fancyPooh.imgType = 'png' fancyPooh.img // 'fancyPooh.png' ``` -- ### Private properties/methods ```js class Meme { #img; constructor(img, text) { this.#img = img; this.text = text; this.#whisper(); } get image() { return this.#img; } set image(newImage) { this.#img = newImage; } #whisper() { console.log('my secret img is', this.#img); } yell() { console.log('MY TEXT IS', this.text.toUpperCase()); } } -- ```js const fancyPooh = new Meme('pooh.jpg', 'so fancy'); // my secret img is pooh.jpg fancyPooh.img // undefined fancyPooh.#img // Error fancyPooh.image // 'pooh.jpg' fancyPooh.image = 'otherPooh.jpg' fancyPooh.#whisper() // Error fancyPooh.yell() // MY TEXT IS SO FANCY ``` -- ### Inheritance ```js class GifMeme extends Meme { constructor(img, text) { if (!img.endsWith('.gif')) { throw new Error('Not a gif'); } super(img, text); } } const failPooh = new GifMeme('pooh.jpg') // Error: Not a gif const gifPooh = new GifMeme('pooh.gif') // my secret img is pooh.gif gifPooh.image // gifPooh ``` --
## Quick Win ### Dark Modal -- ### Let's have a `dialog` ```html
Close
``` -- ### Modal vs. non-modal ```js const dialog = document.querySelector('dialog'); dialog.show(); dialog.showModal(); ``` -- When your user wants to click on something else but you called `.showModal()`
-- ### `dark-modal/index.html` Turn our dark mode toggle into a Modal dialog - Finish the `Form` and `Modal` class definitions - Satisfy the expected behavior