December 07, 2018
Much of the list is comprised from these key sources:
Using local scope, we can actually create new variables with the same name as a variable in an outer scope without changing or reassigning the original value. Variables declared with the var keyword are always function-scoped, meaning they recognize functions as having a separate scope. This locally-scoped variable is therefore not accessible from the global scope.
The new keywords let and const, however, are block-scoped. This means that a new, local scope is created from any kind of block, including function blocks, if statements, and for and while loops.
It is generally recommended that you declare variables that are block-scoped, as they produce code that is less likely to unintentionally override variable values.
After using var to declare a variable, you initialize it with a value. After declaring and initializing, you can then access or reassign the variable. If you attempt to use a variable before it has been declared and initialized, it will return undefined. However, if you omit the var keyword, you are no longer declaring the variable, only initializing it. It will return a ReferenceError and halt the execution of the script.
A closure is the combination of a function and the lexical environment within which that function was declared. This environment consists of any local variables that were in-scope at the time the closure was created.
Closures are useful because they let you associate some data (the lexical environment) with a function that operates on that data. This has obvious parallels to object-oriented programming, where objects allow us to associate some data (the object’s properties) with one or more methods.
Languages such as Java provide the ability to declare methods private, meaning that they can only be called by other methods in the same class.
Closure is when a function can remember and access its lexical scope even when it’s invoked outside its lexical scope.
Closures can trip us up, for instance with loops, if we’re not careful to recognize them and how they work. But they are also an immensely powerful tool, enabling patterns like modules in their various forms.
Modules require two key characteristics: 1) an outer wrapping function being invoked, to create the enclosing scope 2) the return value of the wrapping function must include reference to at least one inner function that then has closure over the private inner scope of the wrapper.
Now we can see closures all around our existing code, and we have the ability to recognize and leverage them to our own benefit!
The enlightenment moment should be: oh, closures are already occurring all over my code, I can finally see them now. Understanding closures is like when Neo sees the Matrix for the first time.
— Kyle Simpson
In a synchronous programming model, things happen one at a time. When you call a function that performs a long-running action, it returns only when the action has finished and it can return the result. This stops your program for the time the action takes.
An asynchronous model allows multiple things to happen at the same time. When you start an action, your program continues to run. When the action finishes, the program is informed and gets access to the result (for example, the data read from disk).
Programming asynchronously is made easier by promises, objects that represent actions that might complete in the future, and async functions, which allow you to write an asynchronous program as if it were synchronous.
One approach to asynchronous programming is to make functions that perform a slow action take an extra argument, a callback function. The action is started, and when it finishes, the callback function is called with the result.
Callbacks are the fundamental unit of asynchrony in JS. But they’re not enough for the evolving landscape of async programming as JS matures.
First, our brains plan things out in sequential, blocking, single-threaded semantic ways, but callbacks express asynchronous flow in a rather nonlinear, nonsequential way, which makes reasoning properly about such code much harder. Bad to reason about code is bad code that leads to bad bugs.
Working with abstract concepts is often easier when those concepts can be represented by values. In the case of asynchronous actions, you could, instead of arranging for a function to be called at some point in the future, return an object that represents this future event.
This is what the standard class promise is for. A promise is an asynchronous action that may complete at some point and produce a value. It is able to notify anyone who is interested when its value is available.
Promises are awesome. Use them. They solve the inversion of control issues that plague us with callbacks-only code.
They don’t get rid of callbacks, they just redirect the orchestration of those callbacks to a trustable intermediary mechanism that sits between us and another utility.
Promise chains also begin to address (though certainly not perfectly) a better way of expressing async flow in sequential fashion, which helps our brains plan and maintain async JS code better. An even better solution to that problem is Async & Await.
The one thing you need to know about async functions is that they always returns a promise.
The await keyword can only be used within an async block, otherwise it’ll throw a syntax error. This means you cannot use await by itself. When do we use it? If we have an asynchronous function inside of an async block. So let’s say we need to fetch some data from our server and then use that data within our async block. We will use await to pause the function execution and resume after the data comes in.
Named after Haskell Brooks Curry, currying is the process of breaking down a function into a series of functions that each take a single argument. It does so with the help of lambda calculus.
Briefly, currying is a way of constructing functions that allows partial application of a function’s arguments. What this means is that you can pass all of the arguments a function is expecting and get the result, or pass a subset of those arguments and get a function back that’s waiting for the rest of the arguments. It really is that simple.
Memoization becomes demystified when you boil it down to key-value pairs. All we’re doing is creating an object, checking for existing values that match the user input and storing new key-value pairs if they don’t exist in our object.
Of course, storing all this data means that we’re going to be using up memory. It’s best to implement memoization on functions that are pure and involve heavy, repetitive calculations.
Polymorphism is the presentation of one interface for multiple data types. For example, integers, floats, and doubles are implicitly polymorphic: regardless of their different types, they can all be added, subtracted, multiplied, and so on. In the case of OOP, by making the class responsible for its code as well as its own data, polymorphism can be achieved in that each class has its own function that (once called) behaves properly for any object.
Just as all methods and properties are defined inside the prototype property, different classes can define methods with the same name; methods are scoped to the class in which they’re defined, unless the two classes hold a parent-child relation (i.e. one inherits from the other in a chain of inheritance).