What is it?
The basic idea of OOP is that we use objects to model real world things that we want to represent inside our programs, and/or provide a simple way to access functionality that would otherwise be hard or impossible to make use of.
OOP breaks down a problem into several entities called objects and builds data and functions around these objects.
Objects
Object fields can be accessed with dot notation (obj.a.b.c) or bracket notation (obj["a"]["b"["c"]). Always use dot notation as long as the field names are known ahead of time. Use bracket notation if using a variable to look up a field ( obj[key]), or if the field name is not a valid JS identifer ( obj["some-field"]).
Reading a field that doesn't exist returns undefined, rather than throwing an error.
Objects can be modified at any time. New fields can be added, existing fields can be reassigned, and fields can be deleted with delete obj.field.
Arrays
Arrays are 0-indexed, but can be sparse - any index can be assigned at any time (arr[97] = "stuff"). Arrays may hold any value and a mixture of different types of values. Like with objects, accessing a non-existing array index returns undefined.
Like with objects, you can use destructuring to read values from arrays (const [a, , c] = ["a", "b", "c"]), and spread operators to create arrays (const arr3 = [...arr1, "b", "c", ...arr2, "d"]).
Arrays are actually objects as well, and have numerous methods built in for various purposes. Some of these methods mutate the existing array, others return new arrays or other values.
To emphasize, array.sort() and array.reverse() mutate the existing array!. This can be very surprising if you're not expecting it. In many cases it's a good idea to make a copy of an array using [...arr] or arr.slice() first and then sort/reverse the copy, especially if you're using something like React or Redux.
The core iteration methods each have a different semantic meaning and purpose:
- map(): creates a new array, with the same size as the original array, whose values are based on a transformation of the original values
- const doubledNumbers = [1, 2, 3].map(num => num * 2)
- filter(): creates a new array, with some of the values from the original, based on values where the comparison returned true:
- const oddNumbers = [1, 2, 3].filter(num => num % 2 === 0)
- forEach(): general iteration over the array, usually to produce side effects
- [1, 2, 3].forEach(num => console.log(num))
- reduce(): calculates a single result value based on "accumulating" a running result at each step, plus a starting value
- const total = [1, 2, 3].reduce( (previousResult, currentValue) => previousResult + currentValue, 0)
Async
Timers and the Event Loop
JS execution is based on a single-threaded event loop + queue. Conceptually, the behavior is while(queue.waitForMessage()) queue.processNextMessage().
Events are added to the queue, and have JS code attached. This includes mouse events, timers, network requests, and much more. The event loop pops the next event off the queue, and executes the entire attached code to completion. The currently executing script cannot be interrupted. The script may start timers, which will add events with scripts to the queue for later execution.
While there is only one JS execution thread, the browser itself may add events to the queue while code is executing. Rather than blocking for async requests, they will schedule events when they complete.
Note that if you have an infinite loop in your code, the event loop is "blocked" and cannot move on to process the next event!
JS logic relies heavily on callbacks - passing a function reference to be called and executed at some future point in time. Callback usage can be synchronous or asynchronous depending on the use case. (slides: callbacks and timers)
JS provides two core functions for scheduling async logic: setTimeout(callback, ms), which runs the callback function once, and setInterval(callback, ms), which runs the callback repeatedly. Both functions return a unique ID value that can be used to cancel the queued timer via clearTimeout() or clearInterval().