JavaScript
Hoisting
All variable declarations are moved to the top of the current scope by default. Initializations are not hoisted.
Example:
x = 5;
console.log(x) // 5
var x; // hoisted
console.log(y) // undefined
var y = 7; // y gets hoisted, so it exists, but value of 7 is not hoisted
Immediately Invoked Function Expression (IIFE)
An anonymous function that is called as soon as it is defined. Pre-ES6 JS only had function scope (not block scope), so an IIFE provides a new execution context, and thus privacy for the variables defined within them.
Common uses:
- The module pattern (singleton) -- Namespacing
- Create closure to 'save' state
- You need a single instance of an object
Example of creating closure:
for (var i = 0; i < 5; i++) {
var btn = document.createElement('button');
btn.appendChild(document.createTextNode('Button ' + i));
(function (i) {
btn.addEventListener('click', function() {console.log(i); });
})(i);
document.body.appendChild(btn);
}
Higher Order Functions
Functions are first class citizens in JS, meaning they can be passed along like any other object. Higher order functions are functions that receive other functions as arguments or return new functions.
Examples:
// function that returns another function
function add(a) {
return function (b) {
return a + b; //uses closure to 'remember', even though it's out of scope
}
}
var add4 = add(4);
add4(6) // 10;
// function that accepts another as an argument
function filter (arr, fn) {
var result = [];
for (var val of arr) {
if (fn(val)) { //calls passed function passed as argument
result.push(val);
}
}
return result;
}
Call and Apply Functions
Call and Apply invoke functions with a specified context, and then pass along a set of arguments. Apply gets arguments in array (someFunction.apply(this, [argsArray]) while call get them individually (someFunction.call(this, arg1, arg2, ...)). Use them to invoke a functions and specify context, chain constructors (inherit properties from parent and assign them), and monkey patching. Use apply for built-in functions (like Math.min, Math.max--although the ...rest operator can also be used).
Bind Function
Creates a new function with specified context and arguments. Mostly just used for passing around 'this'. It's different from call and apply because it just creates a new function which can be called later.
Data Types and Data Structures
MDN Data types and data structures
Primitives types:
- Null
- Undefined
- Boolean
- String
- Number
- Symbol (in ES6+)
Objects
Key value store. Keys are strings or symbols and values can be anything, including other objects. Can be used as hash tables.
Functions
Functions are regular objects with the additional capability of being callable.
Arrays
Arrays are regular objects, but with a special relationship between integer-keyed properties (ie. arr[2]) and the length property. In other words, keys are integers and you use integers to look up values in the array. The length property tracks the number of integer keys.
New to ES6:
- Map
- Set
- WeakMap
- WeakSet
Remember: Primitives are passed by value, objects are passed by reference.
var x = 5;
var y = x;
x = 6;
y // 5;
var obj1 = {a: 1};
var obj2 = obj1;
obj2.a = 2;
obj1.a // 2
function doThing(val) {
val = null;
}
var z = [];
doThing(z);
z // []
Closure
A function remembers its lexical scope when it's executing outside that lexical scope. In other words, functions inside other functions maintain their references to variables in the outer functions even after leaving its scope (as when the first function is called). See example from HOF.
Asynchronous JS
Philip Roberts: What the heck is the event loop anyway?
Asynchronous functions do one of only a few things. Most likely, they listen for events on the DOM (like a mouse click or keypress), a response from an external server (an HTTP request), a response from a database, reading a file, or simply wait for an amount of time to elapse (from a timeout or an interval). At their core, they just listen for an event, and they let other processes continue while they're just sitting around waiting.
The event loop: synchronous code gets placed on the call stack as soon as it is encountered. Async code will not. It will wait for a response, and when it receives the response it's looking for, it places its callback function (which has a reference to the data it was listening for) onto the event queue as soon as it receives the response. _As _a result, if multiple async functions were called, their callback functions get placed in the queue in order of when they receive a response (not in order of when they were called). When all the synchronous code is finished running and the call stack is cleared, the event loop gets triggered, dequeuing the event queue, and moving it's first callback to the call stack and calling it. It keeps doing this until the event queue is cleared.
Be aware that a computationally intensive callback can still block the DOM. Although JS is single threaded, you can still use multiple threads through Webworkers. Thus, an algorithm that requires a lot of calculation can be run using a Webworker, but it's probably better to send it to a backend server.
Inheritance
Although JS is generally thought to have prototypal inheritance, you can argue that it doesn't have inheritance at all. Inheritance implies some sort of "copying" of behavior and properties, which JS doesn't do at all. It's more accurate, and provides a better mental model, to say that JS has behavior delegation. Either way, JS does not have real classes and the ES6 class structure is just syntactic sugar (which can be dangerous). Most languages do not allow the creation of objects directly--they rely on classes to create objects. JS is one of the few where objects are created directly.
this
YDKJS this All Makes Sense Now!
The value of the this keyword will depend entirely on the call-site (how the function is called), not where it's declared. It will then obey the following cascade to determine the value.
- The function was called with 'new'
- The function was called with 'call' or 'apply' specifying an explicit this
- The function was called via a containing/owing object (context)
- DEFAULT: global object (except strict mode--undefined)
ES6+ Features
- ES2015
- Arrow functions
- Classes
- Enhanced object literals
- Template strings
- Destructuring
- Default, rest/gather, spread
- let and const
- Iterators & for...of
- Generators
- Modules (import & export)
- Proxies
- Subclassable built-ins
- Promises
- Math + number + string + array + object APIs
- binary and octal literals
- Recursive tail call optimization
- ES2016
- Array.prototype.includes
- Exponential operator
- ES2017
- Async functions
- Shared memory and atomics
- Object.entries() and Object.values()
- New string methods: padStart and padEnd
- Object.getOwnPropertyDescriptors()
- Trailing commas in function parameter lists and calls