I just finished this book series
about JavaScript, and it gave me a lot of great information and a fresh perspective
on topics in JS that I already kinda-sorta knew, but not really in-depth.
I want to take some time to jot down some notes to help clarify (just for my own sake)
the major concepts I learned from reading the books. In simple one-sentence definitions
and some examples, I think the most important and relevant concepts I learned are:
Pre-ES6
Syntax
try...catch: marks a block of statements to try, and specifies a response, should an exception be thrown
There are three possible forms, including the optional finally block which will execute directly after try and catch but before any subsequent statements. finally executes whether or not an exception was thrown or caught.
try...catch
try...catch...finally
try...finally
Objects
Object.create(proto[, propertiesObject]): creates a new object with the specified prototype object (1st argument) and properties (optional second argument)
Object.keys(): returns an array of all enumerable properties
Object.getOwnPropertyNames(..): returns an array of all properties, enumerable or not.
Arrays
Each of the following methods accepts a function callback but handles the return values differently:
Array.forEach(): iterates over all values in the array, and ignores any callback return values
Array.every(): keeps going until the end or the callback returns a false (or “falsy”) value
Array.some(): keeps going until the end or the callback returns a true (or “truthy”) value
Keywords
throw keyword: stops execution of current function and transfers control to nearest catch block in the call stack (and terminates if there is no catch block)
this keyword: refers to the dynamic scope (where the function was called from), following
four rules, in order of precedence:
New binding: Is the function called with new? If so, this is the newly constructed object
var bar = new foo()
Explicit binding: Is the function called with call or apply, even hidden inside a bind hard binding? If so, this is the explicitly specified object
var bar = foo.call( obj2 )
Implicit binding: Is the function called with a context (implicit binding), otherwise known as an owning or containing object? If so, this is that context object
var bar = obj1.foo()
Default binding: If in strict mode, undefined; otherwise the global object
var bar = foo()
Concepts
Closure: when a function is able to remember and access its lexical scope even when that function is executing outside its lexical scope
When functions are passed around as values, they are most likely exercising closure
Lexical Scope: a set of rules for variable look-ups based on write-time nesting of scopes
Depends on where a function was declared
Dynamic Scope: looks up variables based on the run-time call-stack
Depends on where a function was called from
this exhibits dynamic scope behavior
Module: a small unit of independent, reusable code that can be accessed through a public API
Can be implemented with an IIFE
A “singleton” module can be declared: var foo = (function myModule(){..})();
In the “Revealing Module” pattern, public functions/methods/properties are returned but private variables/functions are not directly accessible
New with ES6
New Keywords
let declarations: create block-scoped variables (not “hoisted”)
When used in the head of for loops, the variables declared with let will be declared for each iteration and be initialized with the value from the end of the previous iteration
let helps resolve closure and block-scoping issues in for loops like below
const declarations: create constants that are set once and cannot be changed
class keyword:
yield keyword: used inside generators to signal a pause point, sends out a value
when pausing the generator, and receives (is replaced by) the eventual resumption value
yield is essentially asking: “What value should I use here? I’ll wait to hear back.”
yield *: Called yield delegation, requires an iterable whose iterator it invokes
and then delegates its own host generator’s control to that iterator until it’s exhausted
New Syntax and Operators
Object.assign(..): Shallow copies a target object (the first parameter) into one or more source objects (subsequent parameters). It iterates over all the enumerable, owned keys (immediately present) on the source object(s) and copies them (via = assignment only) to target. It also, helpfully, returns target
... (Spread/rest operator): gathers together or spreads out
Array/Object Destructuring: decompose object/array values into variables with pattern matching
Nested Destructuring: var { x: { y: { z: w } } } = foo;
Destructuring Parameters
Default Parameter Values: set defaults to function parameters
Concise Methods: use shorter syntax for methods
Computed Property Names: use expressions to compute object property names
for .. of loops: loops over a set of values produced by an iterator
Loops over the values in an iterable, not the keys
Template Literals (String Interpolation): use backticks to enable string interpolation
Interpolated Expressions: you can also put any valid expression inside ${..}, and you can nest the interpolated expressions like so:
Tagged Template (String) Literals: send an interpolated string to a function which receives an array of all the plain strings (not interpolated) as the first argument, and all the results of the evaluated expressions as subsequent arguments:
Arrow Functions ((x) => {..}): Anonymous function expressions that resolves this-binding to be lexical (instead of dynamic)
Most helpful with shorter functions
Removes the need for var self = this hack (but can mess you up if replacing a function already using dynamically-scoped this)
In fact, this, arguments, and super are all lexically-bound with arrow functions
Syntax-level module support: file-based modules (one module per file) using new keywords import and export that bind a reference to that thing (variable, function, etc) like a pointer
You can name a default which sets a particular exported binding to be the default when importing the module
New Concepts / Implementations
Tail Call Optimization (TCO): Proper tail calls (like return foo(x+1) instead of return 1 + foo(x)) are optimized in ES6 so that the extra stack frame allocation is unnecessary, which means there’s practically no limit to how deep the call stack can be
Especially useful for recursion
Promises: a promise of a future value
Resolves the “inversion of control” and “callback hell” problems of callbacks
Promise.resolve(..): creates a promise that is resolved to the value passed in
Promise.reject(..): creates an immediately rejected promise
Promise.all(..): accepts an array of one or more values (immediate values, promises, thenables) and returns a promise that will be fulfilled if all the values fulfill, or reject immediately once the first of any of them rejects
Promise.race(..): accepts an array of one or more values and waits only for either the first fulfillment or rejection
Thenable: any object (or function) with a .then() method on it
Iterator: a structured pattern for pulling information from a source in one-at-a-time fashion
Iterable: any data structure that can produce an iterator (arrays, strings, generator functions, and collections)
Generator: a function that does not necessarily run-to-completion, instead
producing an iterator that will control the generator to execute its code
Useful for two major patterns:
Producing a series of values: This usage can be simple (e.g., random strings or incremented numbers), or it can represent more structured data access (e.g., iterating over rows returned from a database query).
Queue of tasks to perform serially: This usage often represents flow control for the steps in an algorithm, where each step requires retrieval of data from some external source. The fulfillment of each piece of data may be immediate, or may be asynchronously delayed.