Javascript Cheatsheet
Javascript Cheatsheet
Javascript Cheatsheet
ADVANCED
JAVASCRIPT
CONCEPTS
CHEAT SHEET
Zero To Mastery
Link to website
v 10
HEEELLLOOOOO!
I’m Andrei Neagoie, Founder and Lead Instructor of the Zero To Mastery Academy.
We ini ally created this JavaScript Cheat Sheet for Zero To Mastery students taking
our JavaScript: The Advanced Concepts course where they learn modern, advanced JavaScript
prac ces and grow to be in the top 10% of JavaScript developers. We are now making this
available to help all web developers learn and remember common JavaScript concepts.
If you're just star ng to learn JavaScript, congrats! Being a web developer is a fantas c career
op on and industry demand for JavaScript developers is HUGE. Check out my Complete Web
Developer Coding Bootcamp course to learn everything you need to know to become a web
developer.
Already a web developer but stuck in a junior or intermediate role? We can fast track you to get
the knowledge that Senior Javascript Developers have in just 30 days. By the end of
our JavaScript: The Advanced Concepts course, you'll be in the top 10% of JavaScript
Programmers.
Start learning for free with over one hour of free lessons by visi ng the course links above and
click PREVIEW next to the lessons.
Happy Coding!
Andrei
P.S. I also recently wrote a book called Principles For Programmers. You can download the
first five chapters for free here.
Link to website
ti
ti
ti
ti
ti
ti
CREDITS
A huge thanks and credit goes to Zero To Mastery student and Star Mentor, Bri ney,
from her notes while taking and comple ng the JavaScript: The Advanced Concepts
course. Check out some of Bri ney's other fantas c notes on a variety of topics.
Link to website
tt
ti
ti
tt
CONTENTS
JavaScript Engine
The Parser, The AST, The Interpreter, The Compiler, The Combo
Stack Over ow
Garbage Collec on, Synchronous, Event Loop and Callback Queue, Job
Queue, 3 Ways to Promise, Threads, Concurrency, and Parallelism
Execu on Context
Global Execu on Context, Func on Execu on Context, Arrow Func ons
Hois ng
Link to website
Go Back to Table of Contents Page 1 of 93
ti
ti
ti
ti
ti
ti
fl
ti
ti
ti
ti
Lexical Environment
Scope Chain
This
Lexical vs Dynamic Scope
JavaScript Types
Objects in JavaScript, Primi ve vs Non Primi ve, Type Coercion, Sta c vs
Dynamic Typed
ti
ti
ti
ti
ti
The 2 Pillars: Closures and Prototypes
Func on Constructor, Prototypal Inheritance, Prototype vs proto, Callable
Object, Higher Order Func ons, Closures, Memory E cient, Encapsula on
4 Pillars of OOP
Composi on vs Inheritance
OOP Problems, Finally
Modules in JavaScript
Module Pa erns, Issues With Modules, ES6 Modules
Link to website
Go Back to Table of Contents Page 3 of 93
ti
ti
fi
ti
ti
ti
tt
ti
ti
ti
ti
ti
ti
ffi
ti
ti
Error Handling
The End...
Credits
Link to website
Go Back to Table of Contents Page 4 of 93
ti
COURSE MAP
Course Map
JAVASCRIPT ENGINE
A JavaScript engine is a computer program that you give JavaScript code to and it tells
the computer how to execute it. Basically a translator for the computer between
JavaScript and a language that the computer understands. But what happens inside of
the engine? Well, that depends on the engine. There are many JavaScript Engines out
there and typically they are created by web browser vendors. All engines are
standardized by ECMA Script or ES.
ft
tt
THE PARSER
Parsing is the process of analyzing the source code, checking it for errors, and breaking it
up into parts.
THE AST
The parser produces a data structure called the Abstract Syntax Tree or AST. AST is a
tree graph of the source code that does not show every detail of the original syntax, but
contains structural or content-related details. Certain things are implicit in the tree and
do not need to be shown, hence the tle abstract.
THE INTERPRETER
An interpreter directly executes each line of code line by line, without requiring them to
be compiled into a machine language program. Interpreters can use di erent strategies
to increase performance. They can parse the source code and execute it immediately,
translate it into more e cient machine code, execute precompiled code made by a
compiler, or some combina on of these. In the V8 engine, the interpreter outputs
bytecode.
Ni y Snippet: The rst JavaScript engine was wri en by Brendan Eich, the
creator of JavaScript, in 1995 for the Netscape navigator web browser.
Originally, the JavaScript engine only consisted of an interpreter. This later
evolved into the SpiderMonkey engine, s ll used by the Firefox browser.
THE COMPILER
The compiler works ahead of me to convert instruc ons into a machine-code or lower-
level form so that they can be read and executed by a computer. It runs all of the code
and tries to gure out what the code does and then compiles it down into another
language that is easier for the computer to read. Have you heard of Babel or TypeScript?
They are heavily used in the Javascript ecosystem and you should now have a good idea
fi
fi
ffi
ti
ti
ti
ti
tt
ti
ff
of what they are. Babel is a Javascript compiler that takes your modern JS code and
returns browser compa ble JS (older JS code). Typescript is a superset of Javascript that
compiles down to Javascript. Both of these do exactly what compilers do. Take one
language and convert into a di erent one!
THE COMBO
In modern engines, the interpreter starts reading the code line by line while
the pro ler watches for frequently used code and ags then passes is to the compiler to
be op mized. In the end, the JavaScript engine takes the bytecode the interpreter
outputs and mixes in the op mized code the compiler outputs and then gives that to the
computer. This is called "Just in Time" or JIT Compiler.
Memoiza on
Memoiza on is a way to cache a return value of a func on based on its parameters. This
makes the func on that takes a long me run much faster a er one execu on. If the
parameter changes, it will s ll have to reevaluate the func on.
ti
ti
ti
ti
ff
ti
ti
fl
ti
ti
ti
ti
ft
ff
ti
ti
ti
// Bad Way
function addTo80(n) {
console.log('long time...')
return n + 80
}
addTo80(5)
addTo80(5)
addTo80(5)
// long time... 85
// long time... 85
// long time... 85
// Memoized Way
functions memoizedAddTo80() {
let cache = {}
return function(n) { // closure to access cache obj
if (n in cache) {
return cache[n]
} else {
console.log('long time...')
cache[n] = n + 80
return cache[n]
}
}
}
const memoized = memoizedAddTo80()
console.log('1.', memoized(5))
console.log('2.', memoized(5))
console.log('3.', memoized(5))
console.log('4.', memoized(10))
// long time...
// 1. 85
// 2. 85
// 3. 85
// long time...
// 4. 90
Here are a few things you should avoid when wri ng your code if possible:
• eval()
• arguments
• for in
ti
• with
• delete
Inline Caching
function findUser(user) {
return `found ${user.firstName} ${user.lastName}`
}
const userData = {
firstName: 'Brittney',
lastName: 'Postma'
}
findUser(userData)
If this code gets op mized to return only 1 name, then the computer would have to do a
lot more work if you needed to return a di erent user.
Hidden Classes
function Animal(x, y) {
this.x = x;
this.y = y;
}
obj1.a = 30;
obj1.b = 100;
obj2.b = 30;
obj2.a = 100;
Go Backobj1.x
delete to Table= of30;
Contents Page 10 of 93
ti
ff
By se ng these values in a di erent order than they were instan ated, we are making
the compiler slower because of hidden classes. Hidden classes are what the compiler
uses under the hood to say that these 2 objects have the same proper es. If values are
introduced in a di erent order than it was set up in, the compiler can get confused and
think they don't have a shared hidden class, they are 2 di erent things, and will slow
down the computa on. Also, the reason the delete keyword shouldn't be used is
because it would change the hidden class.
function Animal(x, y) {
// instantiating a and b in the constructor
this.a = x;
this.b = y;
}
Managing Arguments
There are many ways using arguments that can cause a func on to be unop mizable. Be
very careful when using arguments and remember:
ti
ff
ff
ti
ti
ti
ti
CALL STACK AND MEMORY HEAP
The JavaScript engine does a lot of work for us, but 2 of the biggest jobs are reading and
execu ng it. We need a place to store and write our data and a place to keep track line
by line of what's execu ng. That's where the call stack and the memory heap come in.
Memory Heap
The memory heap is a place to store and write informa on so that we can use our
memory appropriately. It is a place to allocate, use, and remove memory as needed.
Think of it as a storage room of boxes that are unordered.
const person = {
first: "Brittney",
last: "Postma"
};
ti
ti
ti
Call Stack
The call stack keeps track of where we are in the code, so we can run the program in
order.
function subtractTwo(num) {
return num - 2;
}
function calculate() {
const sumTotal = 4 + 5;
return subtractTwo(sumTotal);
}
debugger;
calculate();
Things are placed into the call stack on top and removed as they are nished. It runs in a
rst in last out mode. Each call stack can point to a loca on inside the memory heap. In
the above snippet the call stack looks like this (see next page).
ti
fi
anonymous; // file is being ran
// CALL STACK
calculate(
// steps through calculate() sumTotal = 9
anonymous
);
// CALL STACK
subtractTwo; // returns 9 - 2
calculate(anonymous);
// CALL STACK
calculate(
// returns 7
anonymous
)(
// CALL STACK
anonymous
);
// CALL STACK
// CALL STACK
STACK OVERFLOW
So what happens if you keep calling func ons that are nested inside each other? When
this happens it’s called a stack over ow.
inception();
// returns Uncaught RangeError:
// Maximum call stack size exceeded
Ni y Snippet: Did you know, Google has hard-coded recursion into their
program to throw your brain for a loop when searching recursion?
Garbage Collec on
JavaScript is a garbage collected language. If you allocate memory inside of a func on,
JavaScript will automa cally remove it from the memory heap when the func on is done
being called. However, that does not mean you can forget about memory leaks. No
system is perfect, so it is important to always remember memory management.
JavaScript completes garbage collec on with a mark and sweep method.
fl
ti
ti
ti
ti
Mark and Sweep Method
var person = {
first: "Brittney",
last: "Postma"
};
In the example above a memory leak is created. By changing the variable person from
an object to a string, it leaves the values of rst and last in the memory heap and does
not remove it. This can be avoided by trying to keep variables out of the global
namespace, only instan ate variables inside of func ons when possible. JavaScript is
a single threaded language, meaning only one thing can be executed at a me. It only
has one call stack and therefore it is a synchronous language.
ti
fi
ti
ti
Synchronous
So, what is the issue with being a single threaded language? Lets's start from the
beginning. When you visit a web page, you run a browser to do so (Chrome, Firefox,
Safari, Edge). Each browser has its own version of JavaScript Run me with a set of Web
API's, methods that developers can access from the window object. In a synchronous
language, only one thing can be done at a me. Imagine an alert on the page, blocking
the user from accessing any part of the page un l the OK bu on is clicked. If everything
in JavaScript that took a signi cant amount of me, blocked the browser, then we would
have a pre y bad user experience. This is where concurrency and the event loop come
in.
tt
ff
ti
fi
fi
ti
ff
ti
ti
fi
tt
ti
ff
console.log("1");
// goes on call stack and runs 1
setTimeout(() => {
console.log("2"), 1000;
});
// gets sent to web api
// web api waits 1 sec, runs and sends to callback queue
// the javascript engine keeps going
console.log("3");
// goes on call stack and runs 3
// event loop keeps checking and see call stack is empty
// event loop sends calback queue into call stack
// 2 is now ran
// 1
// 3
// 2
console.log("1");
setTimeout(() => {
console.log("2"), 0;
});
console.log("3");
// 1
// 3
// 2
In the last example, we get the same output. How does this work if it waits 0 seconds?
The JavaScript engine will s ll send o the setTimeout() to the Web API to be ran and it
will then go into the callback queue and wait un l the call stack is empty to be ran. So,
we end up with the exact same end point.
ti
ff
ti
JS Run me Playground
What the heck is the event loop anyway? | Philip Roberts | JSConf EU (link to YouTube)
ft
ti
ti
ti
ti
The very same Ryan Dahl then gave a talk back in 2018, 10 Things I Regret About
Node.js which led to the recent release of his new (and improved) JavaScript and
TypeScript called Deno which aims to provide a produc ve and secure scrip ng
environment for the modern programmer. It is built on top of V8, Rust, and TypeScript.
If you're interested in learning Deno, Zero To Mastery instructors, Andrei
Neagoie and Adam Odziemkowski (also an o cial Deno contributor), released the
very rst comprehensive Deno course.
ffi
ti
ti
Job Queue
The job queue or microtask queue came about with promises in ES6. With promises we
needed another callback queue that would give higher priority to promise calls. The
JavaScript engine is going to check the job queue before the callback queue.
// 3
console.log("3", "is a crowd");
// 3 is a crowd
// 2 hi
// undefined Promise resolved
// 1 is the loneliest number
// 2 can be as bad as one
3 Ways to Promise
There are 3 ways you could want promises to resolve, parallel (all together), sequen al
(1 a er another), or a race (doesn't ma er who wins).
sequence().then(console.log);
parallel().then(console.log);
race().then(console.log);
// race is done: a
// parallel is done: a b c
// sequence is done: a b c
tt
ti
Threads, Concurrency, and Parallelism
Even though JavaScript is a single threaded language, there are worker threads that
work in the background that don't block the main thread. Just like a browser creates a
new thread when you open a new tab. The workers work through messages being sent,
but don't have access to the full program.
Web Workers
Scaling NodeJS
Mul threading
addEventListener("message");
EXECUTION CONTEXT
Code in JavaScript is always ran inside a type of execu on context. Execu on context is
simply the environment within which your code is ran. There are 2 types of execu on
context in JavaScript, global or func on. There are 2 stages as well to each context, the
crea on and execu ng phase. As the JavaScript engine starts to read your code, it
creates something called the Global Execu on Context.
Crea on Phase
ti
ti
ti
ti
ti
ti
ti
Execu ng Phase
3. Variable Environment created - memory space for var variables and func ons
created
4. Ini alizes all variables to unde ned (also known as hois ng) and places them
with any func ons into memory
this;
window;
this === window;
// Window {...}
// Window {...}
// true
Crea on Phase
Execu ng Phase
3. Variable Environment created - memory space for variable and func ons created
4. Ini alizes all variables to unde ned and places them into memory with any new
func ons
ti
ti
ti
fi
fi
ti
fi
ti
ti
ti
showArgs("hello", "world");
function noArgs() {
console.log('arguments: ', arguments);
}
noArgs();
// arguments: {}
// even though there are no arguments, the object is still created
showArgs("hello", "world");
function showArgs2(...args) {
console.log(console.log("arguments: ", args));
console.log(Array.from(arguments));
return `${args[0]} ${args[1]}`;
}
showArgs2("hello", "world");
Some people think of arrow func ons as just being syntac c sugar for a
regular func on, but arrow func ons work a bit di erently than a regular
func on. They are a compact alterna ve to a regular func on, but also
without its own bindings to this, arguments, super,
or new.target keywords. Arrow func ons cannot be used as constructors
and are not the best op on for methods.
var obj = {
// does not create a new scope
i: 10,
b: () => console.log(this.i, this),
c: function() {
console.log(this.i, this);
}
};
HOISTING
Hois ng is the process of pu ng all variable and func on declara ons into memory
during the compile phase. In JavaScript, func ons are fully hoisted, var variables are
hoisted and ini alized to unde ned, and let and const variables are hoisted but not
ini alized a value. Var variables are given a memory alloca on and ini alized a value of
unde ned un l they are set to a value in line. So if a var variable is used in the code
before it is ini alized, then it will return unde ned. However, a func on can be called
from anywhere in the code base because it is fully hoisted. If let and const are used
ti
tti
fi
ti
ti
ti
ti
tt
ti
fi
ff
ti
ti
ti
ti
ti
ti
ti
// function expression gets hoisted as undefined
var sing = function() {
console.log("uhhhh la la la");
};
// function declaration gets fully hoisted
function sing2() {
console.log("ohhhh la la la");
}
console.log(a());
// bye
foodThoughts();
before they are declared, then they will throw a reference error because they have not
TAKEAWAYS
Avoid hois ng when possible. It can cause memory leaks and hard to catch
bugs in your code. Use let and const as your go to variables.
LEXICAL ENVIRONMENT
A lexical environment is basically the scope or environment the engine is currently
reading code in. A new lexical environment is created when curly brackets {} are used,
even nested brackets {{...}} create a new lexical environment. The execu on context tells
the engine which lexical environment it is currently working in and the lexical scope
determines the available variables.
function one() {
var isValid = true; // local env
two(); // new execution context
}
function two() {
var isValid; // undefined
}
/*
two() isValid = undefined
one() isValid = true
global() isValid = false
------------------------
call stack
*/
ti
SCOPE CHAIN
Each environment context that is created has a link outside of its lexical environment
called the scope chain. The scope chain gives us access to variables in the parent
environment (con nued on next page).
var x = "x";
function findName() {
console.log(x);
var b = "b";
return printName();
}
function printName() {
var c = "c";
return "Brittney Postma";
}
function sayMyName() {
var a = "a";
return findName();
}
sayMyName();
In this example, all the func ons have access to the global variable x, but trying to access
a variable from another func on would return an error. The example below will show
how the scope chain links each func on.
ti
ti
ti
function sayMyName() {
var a = "a";
console.log(b, c); // returns error
return function findName() {
var b = "b";
console.log(a); // a
console.log(c); // returns error
return function printName() {
var c = "c";
console.log(a, b); // a, b
};
};
}
In this example, you can see that the func ons only get access to the variables in their
parent container, not a child. The scope chain only links down the call stack, so you
almost have to think of it in reverse. It goes up to the parent, but down the call stack.
ti
JavaScript is Weird
// It asks global scope for height.
// Global scope says: ummm... no but here I just created it for you.
// We call this leakage of global variables.
// Adding 'use strict' to the file prevents this and causes an error.
function weird() {
height = 50;
}
heyhey();
doodle(); // Error! because it is enclosed in its own scope.
//Function Scope
function loop() {
for (var i = 0; i < 5; i++) {
console.log(i);
}
console.log("final", i); // returns final 5
}
//Block Scope
function loop2() {
for (let i = 0; i < 5; i++) {
// can access i here
}
console.log("final", i); // returns an error here
}
loop();
/*
1
2
3
4
final 5
*/
loop2();
// ReferenceError: i is not defined
ti
ti
ti
ti
LET AND CONST
Variable declara ons with let and const work di erently from
the var variable declara on and I wanted to take a minute to explain. When
a lexical scope is entered and the execu on context is created, the engine
allocates memory for any var variable in that scope and ini alizes it to
unde ned. The let and const variables only get ini alized on the line they
are executed on and only get allocated unde ned if there is no assignment
to the variable. Trying to access a let or const variable before it is declared
or outside of its block without returning it will result in a Reference Error.
ti
ti
ti
ti
fi
ti
fi
ff
ti
ti
ti
ti
THIS
Here we are…
The moment has arrived, me to talk about this. What is this? Why is this so confusing?
For some, this is the scariest part of JavaScript. Well, hopefully we can clear some things
up.
There that's simple right? Well, maybe not, what does that mean? Back in Execu on
Context, we talked about how the JavaScript engine creates the global execu on context
and ini alizes this to the global window object.
function a() {
console.log(this);
}
a();
// Window {...}
ti
ti
ti
ti
ti
In the example above, it is easy to understand that this is equal to the window object,
but what about inside of func on a? Well, what object is func on a apart of? In the dev
tools, if you expand the window object and scroll down the list, you will see a() is a
method on the window object. By calling a(), you are essen ally saying window.a() to
the console.
const obj = {
property: `I'm a property of obj.`,
method: function() {
// this refers to the object obj
console.log(this.property);
}
};
obj.method();
// I'm a property of obj.
obj.method();
function whichName() {
console.log(this.name);
}
const obj1 = {
name: "Obj 1",
whichName
};
const obj2 = {
name: "Obj 2",
whichName
};
whichName(); // window
obj1.whichName(); // Obj 1
obj2.whichName(); // Obj 2
ti
ft
ti
ti
const a = function() {
console.log("a", this);
const b = function() {
console.log("b", this);
const c = {
hi: function() {
console.log("c", this);
}
};
c.hi(); // new obj c called function
};
b(); // ran by a window.a(b())
};
a(); // called by window
// a Window {…}
// b Window {…}
// c {hi: ƒ}
• new keyword binding - the new keyword changes the meaning of this to be the
object that is being created.
• implicit binding - "this" refers to the object that is calling it. It is implied, without
doing anything it's just how the language works.
• explicit binding - using the "bind" keyword to change the meaning of "this".
• arrow func ons as methods - "this" is lexically scoped, refers to it's current
surroundings and no further. However, if "this" is inside of a method's func on, it
falls out of scope and belongs to the window object. To correct this, you can use a
higher order func on to return an arrow func on that calls "this".
ti
ti
// new binding
function Person(name, age) {
this.name = name;
this.age = age;
console.log(this);
}
//implicit binding
const person = {
name: "person",
age: 20,
hi() {
console.log("hi " + this);
}
};
person.hi();
// this = person { name: 'person', age: 20, hi(){...} }
//explicit binding
let name = "Brittney";
const person3 = {
name: "person3",
age: 50,
hi: function() {
console.log("hi " + this.name);
}.bind(window)
};
person3.hi();
// hi Brittney
// this = window {...}
person4.hi();
// this = person4 { name: 'person4', age: 40, hi() {...} }
// if either function is changed around, it doesn't work
const obj = {
name: "Billy",
sing() {
console.log("a", this);
var anotherFunc = function() {
console.log("b", this);
};
anotherFunc();
}
};
obj.sing();
In the example above, the obj called sing() and then anotherFunc() was called within the
sing() func on. In JavaScript, that func on defaults to the Window object. It happens
because everything in JavaScript is lexically scoped except for the this keyword. It
doesn't ma er where it is wri en, it ma ers how it is called. Changing anotherFunc()
instead to an arrow func on will x this problem, as seen below. Arrow func ons do not
bind or set their own context for this. If this is used in an arrow func on, it is taken from
the outside. Arrow func ons also have no arguments created as func ons do.
const obj = {
name: "Billy",
sing() {
console.log("a", this);
var anotherFunc = () => {
console.log("b", this);
};
anotherFunc();
}
};
obj.sing();
ti
tt
ti
ti
ti
tt
tt
fi
ti
tt
ti
ti
ti
ti
var b = {
name: "jay",
say() {
console.log(this);
}
};
var c = {
name: "jay",
say() {
return function() {
console.log(this);
};
}
};
var d = {
name: "jay",
say() {
return () => console.log(this);
}
};
A er everything is said and done, using this can s ll be a bit confusing. If you aren't sure
what it's referencing, just console.log(this) and see where it's poin ng.
ti
const wizard = {
name: "Merlin",
health: 100,
heal(num1, num2) {
return (this.health += num1 + num2);
}
};
const archer = {
name: "Robin Hood",
health: 30
};
console.log(archer); // health: 30
In this example call is used to borrow the heal method from the wizard and is used on
the archer (which is actually poin ng this to archer), with the op onal arguments added.
Apply
Apply is almost iden cal to call, except that instead of a comma separated list of
arguments, it takes an array of arguments.
// instead of this
// wizard.heal.call(archer, 50, 20)
// apply looks like this
wizard.heal.apply(archer, [50, 20]);
// this has the same result
ti
ti
ti
ff
ti
Bind
Unlike call and apply, bind does not run the method it is used on, but rather returns a
new func on that can then be called later.
console.log(archer); // health: 30
const healArcher = wizard.heal.bind(archer, 50, 20);
healArcher();
console.log(archer); // health: 100
function multiply(a, b) {
return a * b;
}
function getMaxNumber(arr) {
return Math.max.apply(null, arr);
}
getMaxNumber(array); // 3
ti
ti
ti
ti
Exercise 2: How would you x this?
const character = {
name: "Simon",
getCharacter() {
return this.name;
}
};
const giveMeTheCharacterNOW = character.getCharacter;
JAVASCRIPT TYPES
Bri ney goes into all of the types in her basic JavaScript course notes, but decided to
take a deeper dive into types in JavaScript here.
Type Result
Unde ned unde ned
Null object*
Boolean boolean
Number number
BigInt (new in ECMAScript 2020) bigint
String string
Symbol (new in ECMAScript 2015) symbol
Func on object function
Any other object object
*Null - Why does the typeof null return object? When JavaScript was rst
implemented, values were represented as a type tag and a value. The
objects type tag was 0 and the NULL pointer (0x00 in most pla orms)
consequently had 0 as a type tag as well. A x was proposed that would
have made typeof null === 'null', but it was rejected due to legacy code that
would have broken.
// Numbers
typeof 37 === "number";
typeof 3.14 === "number";
typeof 42 === "number";
typeof Math.LN2 === "number";
typeof Infinity === "number";
typeof NaN === "number"; // Despite being "Not-A-Number"
typeof Number("1") === "number"; // Number tries to parse things into numbers
typeof Number("shoe") === "number"; // including values that cannot be type coerced to a
number
tt
fi
ti
fi
fi
tf
fi
// Strings
typeof "" === "string";
typeof "bla" === "string";
typeof `template literal` === "string";
typeof "1" === "string"; // note that a number within a string is still typeof string
typeof typeof 1 === "string"; // typeof always returns a string
typeof String(1) === "string"; // String converts anything into a string, safer than toString
// Booleans
typeof true === "boolean";
typeof false === "boolean";
typeof Boolean(1) === "boolean"; // Boolean() will convert values based on if they're truthy or
falsy
typeof !!1 === "boolean"; // two calls of the ! (logical NOT) operator are equivalent to Boolean()
// Symbols
typeof Symbol() === "symbol";
typeof Symbol("foo") === "symbol";
typeof Symbol.iterator === "symbol";
// Undefined
typeof undefined === "undefined";
typeof declaredButUndefinedVariable === "undefined";
typeof undeclaredVariable === "undefined";
// Objects
typeof { a: 1 } === "object";
// Functions
typeof function() {} === "function";
typeof class C {} === "function";
typeof Math.sin === "function";
Objects in JavaScript
Objects are one of the broadest types in JavaScript, almost "everything" is an
object. MDN Standard built-in objects
• string
• number
• bigint
• boolean
• null
• unde ned
• symbol
ti
fi
ti
fi
ti
ti
fi
ti
fi
fi
fi
ti
fi
fi
ti
fi
ti
ti
Non Primi ve - The only type that leaves us with is objects. Objects are able to be
mutated and their proper es are passed by reference, meaning their proper es are not
stored separately in memory. A new variable poin ng to an object will not create a copy,
but reference the original objects loca on in memory. Therefore, changing the 2nd
object will also change the rst.
There are two ways to get around this, Object.assign() or use the spread operator {...} to
"spread" or expand the object into a new variable. By doing this, it will allow the new
variable to be modi ed without changing the original. However, these only create a
"shallow copy".
> Deep copy: A deep copy copies all elds, and makes copies of dynamically
allocated memory pointed to by the elds. A deep copy occurs when an
object is copied along with the objects to which it refers.
fi
ti
fi
fi
fi
ti
ti
ti
const originalObj = {
nested: {
nestedKey: "nestedValue"
},
key: "value"
};
// originalObj points to location 1 in memory
const assignObj = originalObj;
// assignObj will point to 1 in memory
const shallowObj = { ...originalObj };
// shallowObj points to a new location 2, but references location 1 for the nested
object
const deepObj = JSON.parse(JSON.stringify(originalObj));
// deepObj clones all parts of the object to a new memory address
const originalObj = {
nested: {
nestedKey: "nestedValue"
},
key: "value"
};
const assignObj = originalObj;
const shallowObj = { ...originalObj };
const deepObj = JSON.parse(JSON.stringify(originalObj));
/*
originalObj: {nested: {
nestedKey: "changed value"
},
key: "changed value"}
assignObj: {nested: {
nestedKey: "changed value"
},
key: "changed value"}
shallowObj: {nested: {
nestedKey: "changed value"
},
key: "value"}
deepObj: {nested: {
nestedKey: "nestedValue"
},
key: "value"}
*/
Type Coercion
Type coercion is the process of conver ng one type of value into another. There are 3
types of conversion in JavaScript.
• to stringx
• to boolean
• to number
let num = 1;
let str = "1";
num == str; // true
// notice loose equality ==, not ===
// double equals (==) will perform a type conversion
// one or both sides may undergo conversions
// in this case 1 == 1 or '1' == '1' before checking equality
Strict equals: The triple equals (===) or strict equality compares two values
without type coercion. If the values are not the same type, then the values
are not equal. This is almost always the right way to check for equality in
JavaScript, so you don't accidentally coerce a value and end up with a bug in
your program. Here is the MDN Equality Comparison page and
the ECMAScript Comparison Algorithm.
ft
ti
ti
ti
There are several edge cases that you will come in contact with in JavaScript as well.
Check out this Comparison Table if you have ques ons about how types are coerced.
Func on Constructor
Func ons are objects in JavaScript, which is not true for other languages. Because of
that, they can be called mul ple ways, but they can also be constructors. A func on
constructor creates a new object and returns it. Every JavaScript func on, is actually a
func on object itself.
ti
ti
ti
ti
fl
ti
ti
ti
ti
ti
ft
// function constructor
new Function("optionalArguments", "functionBody");
Almost everything in JavaScript can be created with a constructor. Even basic JavaScript
types like numbers and strings can be created using a constructor.
Prototypal Inheritance
Almost all objects in Javascript pass down proper es through a prototype chain. We call
this chain, prototypal inheritance. The child of the object "inherits" proper es from its
parent. All objects in JavaScript are descended from the Object constructor unless
deliberately created or altered to not do so. The objects inherit methods and proper es
from Object.prototype. The prototype property also has an accessor property
called __proto__ that creates a link between the current object and points to the object
it was created from, the "prototype".
Object.prototype.__proto__;
// null
Object.prototype;
{
__proto__: null;
// ...more methods and properties
}
Object;
// function Object()
// This is the object constructor function
Object.prototype.constructor;
// function Object()
// Points to the constructor
Object.__proto__;
// function () {...}
// Because it is created with a constructor function
Prototype vs __proto__
Understanding the di erence between __proto__ and prototype can be quite a
confusing concept for JavaScript developers. Every func on in JavaScript automa cally
gets a prototype property when it is created that gives it the call, apply, and bind
methods. It doesn't really do anything with regular func ons, but in constructor
ti
ti
ti
ti
ti
ti
func ons the prototype property allows us to add our own methods to the objects we
create. The __proto__ property is what creates the link between prototype objects, the
child inherits proper es from the parent through the prototype chain. Each me a new
object is created in JavaScript, it uses the __proto__ ge er func on to use a built in
constructor func on based on what is being created. This could be an Array, Boolean,
Date, Number, Object, String, Func on, or RegExp. Each one has their own separate
proper es and methods that they inherit from the constructor.
ti
ti
Callable Object
Because func ons are objects in JavaScript, this also gives them the ability to have
proper es added to them. This creates a callable object, a special object that creates
proper es not available on normal objects. Below is a visualiza on of how this works
under the hood. This code can not be ran in the console, but it is a representa on of
how the object looks.
function say() {
console.log('say something')
}
// with an obj
const obj = {
// nothing gets created
}
ti
ti
Ni y snippet: You might hear people say "Func ons are rst-class ci zens in
JavaScript". All this means is that func ons can be passed around as if they
were a JavaScript type. Anything that can be done with other 7n hhb , can
also be done with func ons. This introduces JavaScript to a whole di erent
type of programming called func onal programming. Below are some
examples of how func ons work di erently in JavaScript.
• func on ()
• func on (a,b)
• func on hof() { return func on () {} }
Instead of wri ng mul ple func ons that do the same thing, remember DRY (don't
repeat yourself). Imagine in the example below, if you separated each code out into
individual func ons how much more code you would be wri ng and how much code
would be repeated.
ti
ti
ti
ti
ti
ti
ti
ti
ti
ti
ti
ff
ti
ti
ti
ti
fi
ti
ti
ti
ff
function auth(roleAmt) {
let array = [];
for (let i = 0; i < roleAmt; i++) {
array.push(i);
}
return true;
}
Take the example below of how you can separate code out and break it down to make it
more reusable.
function multBy(a) {
return function(b) {
return a * b;
};
}
multByTwo(4); // 8
multByTen(5); // 50
Closures
Closures allow a func on to access variables from an enclosing scope or environment
even a er it leaves the scope in which it was declared. In other words, a closure gives
you access to its outer func ons scope from the inner scope. The JavaScript engine will
keep variables around inside func ons that have a reference to them, instead of
"sweeping" them away a er they are popped o the call stack.
function a() {
let grandpa = 'grandpa'
return function b() {
let father = 'father'
let random = 12345 // not referenced, will get garbage collected
return function c() {
let son = 'son'
return `closure inherited all the scopes: ${grandpa} > ${father} > ${son}`
}
}
}
a()()()
// closure inherited all the scopes: grandpa > father > son
const closure = grandma => mother => daughter => return `${grandma} > ${mother} > ${daughter}
`
ft
ti
ft
ti
ti
ff
function callMeMaybe() {
const callMe = `Hey, I just met you!`
setTimeout(function() {
console.log(callMe)
}, 8640000000);
callMeMaybe()
Two of the major reasons closures are so bene cial are memory e ciency and
encapsula on.
fi
ti
ffi
Memory E cient
Using closures makes your code more memory e cient. Take the example below.
function inefficient(idx) {
const bigArray = new Array(7000).fill("😄 ");
console.log("created!");
return bigArray[idx];
}
function efficient() {
const bigArray = new Array(7000).fill("😄 ");
console.log("created again!");
return function(idx) {
return bigArray[idx];
};
}
inefficient(688);
inefficient(1000);
inefficient(6500);
getEfficient(688);
getEfficient(1000);
getEfficient(6500);
// created!
// created!
// created!
// created Again!
// '😄 '
ffi
Encapsula on
Encapsula on means the restric on of direct access to some of an object's components.
It hides as much as possible of an object's internal parts and only exposes the necessary
parts to run. Why use encapsula on?
ti
ti
ti
ti
fi
const elf1 = {
name: 'Dobby',
type: 'house',
weapon: 'cloth',
say: function() {
return `Hi, my name is ${this.name}, I am a ${this.type} elf.`
}
attack: function() {
return `attack with ${this.weapon}`
}
}
const elf2 = {
name: 'Legolas',
type: 'high',
weapon: 'bow',
say: function() {
return `Hi, my name is ${this.name}, I am a ${this.type} elf.`
}
attack: function() {
return `attack with ${this.weapon}`
}
}
elf1.attack()
// attack with cloth
elf2.attack()
// attack with bow
ti
ti
tti
ti
ti
ti
Stores
This is a step in the right direc on, but if we added more characters, we would run into
some of the same issues again. Not only is the code not DRY, the a ack method is being
created and taking up memory space for every new elf. This is not very e cient. How do
we solve this? Well, we could separate the methods out into a store.
const elfMethodsStore = {
attack() {
return `attack with ${this.weapon}`;
},
say() {
return `Hi, my name is ${this.name}, I am a ${this.type} elf.`;
}
};
ti
tt
ffi
Object.create
Having a store saved us some e ciency in memory, but this was a lot of manual work to
assign each method. So, we were given Object.create to help create this chain without
having to assign each method.
const elfMethodsStore = {
attack() {
return `attack with ${this.weapon}`;
},
say() {
return `Hi, my name is ${this.name}, I am a ${this.type} elf.`;
}
};
ti
ti
ffi
ti
ti
func on. Without new, this will point to the window object instead of the object that we
just created. It is best prac ce to capitalize constructor func ons to help us iden fy
them and know to use the new keyword. Proper es added to a constructor func on can
only be done using the this keyword, regular variables do not get added to the object.
Class
Confused yet? Prototype is a li le weird and hard to read unless you really understand
your prototypal inheritance. No one really liked using the prototype way of adding
methods, so in ES6 JavaScript gave us the class keyword. However, classes in JavaScript
are not true classes, they are syntac c sugar. Under the hood, it is s ll using the old
prototype method. They are in fact just "special func ons" with one big di erence,
func ons are hoisted and classes are not. You need to declare your class before it can be
used in your codebase. Classes also comes with a new method, the constructor that
creates and instan ates an object created with class. Classes are able to be extended
upon using the extends keyword, allowing subclasses to be created. If there is a
constructor present in the extended class, the super keyword is needed to link the
constructor to the base class. You can check if something is inherited from a class by
using the keyword instanceof to compare the new object to the class.
ti
tt
ti
ti
ti
ti
ff
class Character {
constructor(name, weapon) {
this.name = name;
this.weapon = weapon;
}
attack() {
return `attack with ${this.weapon}`;
}
}
// public declarations
class Rectangle {
height = 0;
width;
constructor(height, width) {
this.height = height;
this.width = width;
}
}
// private declarations
class Rectangle {
#height = 0;
#width;
constructor(height, width) {
this.#height = height;
this.#width = width;
}
}
So, did we obtain perfect object oriented programming? Well, that is up for debate. It is
really up to you the developer to decide which style of wri ng you like best. We did
learn that object oriented programming helps make you code more understandable,
easy to extend, easy to maintain, memory e cient, and DRY!
ffi
ti
ti
ti
fi
Ni y Snippet: Why didn't Eich just add classes to JavaScript in the
beginning?
"If I had done classes in JavaScript back in May 1995, I would have been told
that it was too much like Java or the JavaScript was compe ng with Java ... I
was under marke ng orders to make it look like Java but not make it too big
for its britches ... [it] needed to be a silly li le brother language." —Brendan
Eich
4 PILLARS OF OOP
• Encapsula on - Organizes code into containers that relate to each other and makes
it easier to maintain and reuse.
• Abstrac on - Hides the complexity from the user by doing the method calcula ons
behind the scenes.
• Inheritance - Gives the proper es of a class to another class, keeping code DRY and
saving on memory space.
• Polymorphism - The ability of an object to take on many forms allowing methods to
be used di erently by di erent classes.
ti
ti
ff
ti
ff
ti
tt
ti
ti
FUNCTIONAL PROGRAMMING
Func onal programming has the same goals in mind as object oriented programming, to
keep your code understandable, easy to extend, easy to maintain, memory e cient, and
DRY. Instead of objects, it uses reusable func ons to create and act on data. Func onal
program is based on a separa on of concerns similar to object oriented programming.
However, in func onal programming there is a complete separa on between the data
and the behaviors of a program. There is also an idea that once something is created, it
should not be changed, being immutable. Unlike OOP, shared state is avoided func onal
programming works on the idea of pure func ons.
Build lots of very small, reusable and predictable pure func ons that do the following:
• Complete 1 task per func on.
• Do not mutate state.
• Do not share state.
• Be predictable.
• Be composable, one input and one output.
• Be pure if possible.
• Return something.
Referen al transparency
One important concept of func onal programming is referen al transparency, the
ability to replace an expression with the resul ng value without changing the result of
the program.
ti
ti
ti
ti
ti
ti
ti
ti
ti
ff
ti
ti
ti
ti
ti
ff
ti
ti
ti
ti
ff
ffi
ti
ti
function b(num) {
return num * 2;
}
b(a(3, 4)); // 14
// a should always return 7
// so it could be changed to
b(7); // 14
// and the output is the same
Idempotence
Idempotence is another important piece of func onal programming. It is the idea that
given the same input to a func on, you will always return the same output. The func on
could be used over and over again and nothing changes. This is how you make your code
predictable.
Impera ve vs Declara ve
Impera ve programming tells the computer what to do and how to complete it.
Declara ve programming only tells the computer what to do, but not how to do things.
Humans are declara ve by nature, but computers typically need more impera ve type
programming. However, using higher level languages like JavaScript is actually being less
declara ve. This is important in func on programming because we want to be more
declara ve to be er understand our code and let the computer handle the dirty work of
guring out the best way to do something.
fi
ti
ti
ti
ti
ti
tt
ti
ti
ti
ti
ti
ti
ti
// more imperative
console.log(i);
// more declarative
Immutability
Immutability is simply not modifying the original data or state. Instead we should create
copies of the state inside our func ons and return a new version of the state.
// Bad code
const obj = {name: 'Brittney'}
function clone(obj) {
return {...obj} // this is pure
}
// Better code
function updateName(obj) {
const newObj = clone(obj)
newObj.name = 'Joe'
return newObj
}
You may be thinking that this could get really expensive, memory wise, to just copy code
over and over. However, there is something called structural sharing that allows the data
to only copy new informa on and points to the original state for any commonali es.
ti
ti
ti
Par al Applica on
Par al applica on is expanding on the idea of currying and taking it a step farther by
separa ng a parameter out. If you have more than 2 arguments in a func ons, then you
can bind one of them to a value to be used later.
ti
ft
ffi
ti
ti
ti
ft
ti
Ni y Snippet: The Pipeline Operator is in the experimental stage 1 of being
introduced to JavaScript. Stage 1 means that it has only started the process
and could be years before it is a part of the language. The pipeline
operator, |>, would be syntac c sugar for composing and piping func ons
the long way. This would improve readability when chaining mul ple
func ons.
Arity
Arity simply means the number of arguments a func on takes. The more parameters a
func on has the harder it becomes to break apart and reuse. Try to s ck to only 1 or 2
parameters when wri ng func ons.
ti
ti
ti
ti
ff
ti
ti
ti
ti
ti
ti
ti
ti
ti
ti
ti
const user = {
name: "Kim",
active: true,
cart: [],
purchases: []
};
function taxItems(user) {
userHistory.push(
Object.assign({}, user, { cart: user.cart, purchases: user.purchases })
);
const { cart } = user;
const taxRate = 1.4;
const updatedCart = cart.map(item => {
return {
name: item.name,
price: item.price * taxRate
};
});
return Object.assign({}, user, { cart: updatedCart });
}
function buyItems(user) {
userHistory.push(
Object.assign({}, user, { cart: user.cart, purchases: user.purchases })
);
return Object.assign({}, user, { purchases: user.cart });
}
function emptyCart(user) {
userHistory.push(
Object.assign({}, user, { cart: user.cart, purchases: user.purchases })
);
return Object.assign({}, user, { cart: [] });
}
purchaseItems(
emptyCart,
buyItems,
taxItems,
addToCart
)(user, { name: "laptop", price: 200 });
Go Back to Table of
console.log(userHistory); Contents Page 78 of 93
COMPOSITION VS INHERITANCE
Composi on is what we just did with FP, crea ng small reusable func ons to make code
modular. Inheritance is what we did with OOP, crea ng a class and extending it to
subclasses that inherit the proper es. In OOP we create few opera ons on common data
that is stateful with side e ects. In FP we create many opera ons on xed data with pure
func ons that don't mutate state. There is a big debate over which one is be er and
most people believe that composi on is be er.
OOP Problems
One of the drawbacks to inheritance is that it is based on the fact that it won't change,
we tell it what it is. We create a class and give it proper es and methods that describe
the class. But say, down the road, we need to update that class and add more
func onality. Adding a new method to the base class will create rippling e ects through
your en re program. FP is more declara ve, what to do not how, and OOP is more
impera ve, what and how to do something. This is the ght coupling problem, things
having to depend on one another, which leads to the fragile base class problem,
seemingly safe changes cause unforeseen repercussions. It is the opposite of small
reusable code. Changing one small thing in either of the class or subclasses could break
the program. Another problem is hierarchy where you may need to create a subclass
that can only do 1 part of the class, but instead you get everything passed down to it.
Finally
Composi on is probably a be er tool to use when crea ng programs because it creates
a more stable environment that is easier to change in the future. The key is to decide
which structure is be er for your project. You can use ideas from both of these styles to
write your code. React uses OOP in class components to extend inheritance and then
uses FP in the pure components.
ti
ti
tt
ff
tt
ti
ti
ti
tt
ti
ti
ti
ti
ti
ti
ti
fi
ti
ff
tt
MODULES IN JAVASCRIPT
Modules are pieces of code, grouped together, that can be combined together to create
an expandable program that can get bigger as it needs to. Good modules are self
contained and grouped together with their own speci c func onality allowing them to
be moved or deleted without breaking the program.
Module Pa erns
Originally in JavaScript, we had the module pa ern. Before block scope came around,
there was only global scope and func on scope. To create this idea of modules,
a module scope was implemented just above the func on scope. This allowed variables
to be shared, by expor ng and impor ng, between the func ons without having to go
through the global scope. A func on as a module is essen ally just an immediately
invoked func on expression, IIFE.
ti
ti
ti
ti
tt
fi
ti
ti
ti
ti
Issues with Modules
Even though modules help us to contain and organize code, there are s ll problems that
can arise. There can be naming con icts if you don't use const to declare the module.
Also, there are dependency issues if scripts are placed in the wrong order, such as jQuery
needing to be called before it can be used. Because of these problems, people started
developing libraries to solve them. Before ES6 we had 2 ways to implement modules in
JavaScript CommonJS and AMD.
• CommonJS - uses the keywords require and exports to interact with the module
system. Require is a func on used to import from another module and exports is an
object where func ons get exported from. These are run synchronously where we
wait on one module to load before another can start and this is not ideal for browsers.
However, this code may look familiar because NodeJS s ll uses this library. There are
other packages such as Browserify and webpack that aid in bundling scripts with
CommonJS to be used in the browsers.
• Asynchronous Module De ni on (AMD) - as in the name, AMD loads modules
asynchronously. This was great for browsers early on before packages that bundled
code.
de ne(['module1', 'module2'], func on(module1, module2)
{console.log(module1.setName());});
The de ne func on takes an array of dependency modules that are loaded in a non-
blocking manner in the background. Once completed, the callback func on is then
executed. Packages came out like RequireJS that implemented the AMD endpoint and
was the main way people used AMD modules.
ES6 Modules
A er ES6 came out, pre y much everything above was thrown out the window with 2
new keywords. We can now use the import and export keywords in our les to
implement modules. This again may look familiar from popular frameworks like React.
ti
tt
ti
fi
ti
fl
ti
ti
ti
ti
fi
Here is our module code from above in the new ES6 syntax.
There are 2 types of exports, named and default. A named export is imported using curly
braces ({ importFnName }) and a default func on is added in created like this:
Trying to run this in the browser there is s ll 2 more things that have to be done. You
have to declare the type in the html script tag as module and the le has to be served
from a server. You can spin up your own server with a package like live-server on npm.
fi
ERROR HANDLING
One of the most important things to learn in being a developer is how to solve errors.
Learning to handle errors makes you a be er programmer. Wri ng your programs you
have the ability to use the throw keyword to stop the program and handle an error by
using a try/catch block that has an op onal nally block or the .catch() method for
asynchronous code. Throwing a new error in asynchronous code gets what is called a
silent fail if there is no catch block present. In synchronous code, if there is no catch
statement placed in the code, the run me will create catch: onerror() and we see the
built in JavaScript error message in red (see next page).
tt
fi
ti
throw new Error();
fail();
// this works // because it goes line by line
// we have made an oopsie Error: oopsie at fail
// still good
// asynchronous .catch()
Promise.resolve("asyncfail")
.then(response => {
console.log(response);
return response;
})
.catch(error => {
console.log(err);
});
(async function() {
try {
await Promise.resolve("oopsie #1");
await Promise.reject("oopsie #2");
} catch (err) {
console.log(err);
}
console.log("is this still good?");
})();
Errors created using the new keyword come with 3 proper es.
myError.name; // "Error"
myError.message; // "oopsie"
myError.stack; // "Error: oopsie at <anonymous>:1:17
function a() {
const b = new Error("uh oh");
return b;
}
b(); // b().stack
// Error: uh oh
// at a (<anonymous>:2:12)
// at <anonymous>:1:1
ti
ti
Because Error is a constructor func on, we can use that to extend it and add to it. You
don't want to reveal parts of your program by allowing an error to give the stack trace
and other informa on to possible bad actors. So, you can customize what you want your
errors to reveal.
THE END…
This is the "o cial" end of the Advanced JavaScript sec on, but Bri ney added a small
sec on of her notes on data structures and algorithms because they are an important
part of developing great programs.
ti
tt
ti
ti
tt
Data Structures
A data structure is di erent types of containers that hold your data. Each container has
its own type of data it holds and its speci c to that type. You want to be able to easily
access your data and know where it is located. It is basically a way to organize your data.
There are 2 parts to data structures, how to build one and how to use it.
ff
ti
ff
ti
fi
tt
ff
ti
ti
ti
a list of data structures that are built into several popular languages. That doesn't mean
that you can't use other types, you just have to build our own. Such as if JavaScript
doesn't have stacks, we can build one.
Arrays
Arrays order items sequen ally with an index. Arrays are probably the simplest and the
most widely used data structure because the are fast and take up the least amount of
space. They also have the least amount of rules. Array methods have di erent me
complexi es, called Big-Order or Big-O nota ons. _O(1) is constant me, meaning the
me does not change with the data input. The _O(n) is linear me, meaning me
changes or goes up the more opera ons that need to be performed. _O(1) can end up as
_O(n) in languages like JavaScript if it needs to allocate more memory for the array.
There is also, Big-Omega or Big-Ω nota on that give the best possible me for your
ti
ti
ti
ti
ti
ti
ti
ti
ff
ti
ti
program. If a program has the same me complexity in Big-O and in Big-Ω, then you can
use θ as shorthand for both combined.
strings[2]; // c // O(1)
strings.push("e"); // O(1)
// ['a', 'b', 'c', 'd', 'e']
strings.pop(); // O(1)
// ['a', 'b', 'c', 'd']
strings.unshift("z"); // O(n)
// ['z', 'a', 'b', 'c', 'd']
// unshift took 5 operations to complete.
// ['a', 'b', 'c', 'd']
// [ 0 1 2 3 ] all indexes need to shift
// ['z', 'a', 'b', 'c', 'd']
// [ 0 1 2 3 ]
// [ 0 1 2 3 4 ]
Implemen ng an Array
Arrays can be declared easily in JavaScript, but what if we built our own…
ti
class MyArray {
constructor() {
this.length = 0;
this.data = {};
}
get(index) {
return this.data[index];
}
push(item) {
this.data[this.length] = item;
this.length++;
return this.length;
}
pop() {
const lastItem = this.data[this.length - 1];
delete this.data[this.length - 1];
this.length--;
return lastItem;
}
delete(index) {
const item = this.data[index];
this.shiftItems(index);
return item;
}
shiftItems(index) {
for (let i = index; i < this.length; i++) {
this.data[i] = this.data[i + 1];
}
delete this.data[this.length - 1];
this.length--;
}
}
Hash Tables
Di erent languages have di erent names for a hash table, but in JavaScript a hash table
is an object. A data structure that stores data in key/value pairs.
Hash Func on
A hash func on takes a key and maps it to a value of xed length for every input. It is an
idempotent func on meaning given the same input the output will always be the same.
A hash table uses the hash func on to compute the key into a hash code and map that
to an address in memory where it is stored with the value in a bucket. Using the hashing
technique makes looking up data inside the hash table very fast and is usually O(1) me.
let character = {
age: 20,
name: "Harry Potter",
muggle: false,
patronus: function() {
console.log("Expecto Patronum!");
}
};
character.age; // 20 // O(1)
character.levitate = "Wingardium Leviosa!"; // O(1)
character.patronus(); // Expecto Patronum! // O(1)
Hash Collisions
Every data structure is going to come with downsides. Hash collisions are what happens
when a hash func on maps a key to the same address as a previously added key. With
enough data and limited memory, we will always run into this collision. This does not
overwrite the previous informa on, but creates a linked list and slows down our ability
to access the informa on. Your big O nota on me jumps from O(1) to O(n/k) where n is
the me and k is the size of the hash table.
ti
ti
ti
ff
ti
ti
ti
ti
fi
ti
Hashing in JavaScript
JavaScript came out with 2 ways to help prevent hash collisions when implemen ng
hash tables, the Map object and the Set. Map will store key/value pairs like an object,
but will remember the original order in memory. A Map also allows for any data type to
be stored as a key such as an array or func on. A Set will only store the values and also
remembers the original order, but the values may only occur once.
ti
ti
class HashTable {
constructor(size) {
this.data = new Array(size);
// this.data = [];
}
_hash(key) {
let hash = 0;
for (let i = 0; i < key.length; i++) {
hash = (hash + key.charCodeAt(i) * i) % this.data.length;
}
return hash;
}
set(key, value) {
let address = this._hash(key);
if (!this.data[address]) {
this.data[address] = [];
}
this.data[address].push([key, value]);
return this.data;
}
get(key) {
const address = this._hash(key);
const currentBucket = this.data[address];
if (currentBucket) {
for (let i = 0; i < currentBucket.length; i++) {
if (currentBucket[i][0] === key) {
return currentBucket[i][1];
}
}
}
return undefined;
}
keys() {
const keysArray = [];
for (let i = 0; i < this.data.length; i++) {
if (this.data[i]) {
keysArray.push(this.data[i][0][0]);
}
}
return keysArray;
}
}