JavaScript Cheatsheet (Q&A — Concepts You Need to Know to Pass Tech Interview)
--
Share some coding notes I took in the past that helped me understand the concepts, and these are questions you may be asked during interviews.
data type
primitive
variable stores the values referred to by the valuenon-primitive
object points to a reference (memory location of the value), not the value itself
typeof 42 //number
typeof "abc" //string
typeof true //boolean
typeof Boolean(1) //boolean
typeof undefined //undefined
typeof null //object (js built-in error)
typeof {"a": 1} //object
typeof [1, 2, 3] //object
typeof function hello(){} //function
false values
false, 0, -0, 0n, “”, null, undefined, and NaN
=, ==, ===
=
- assignment operator, which sets the variable on the left of the = to the value of the expression that is on its right==
- comparison operator, which transforms the operands having the same type before the comparison2=='2'
===
- strict equality comparison operator, which returns false for the values which are not of a similar type2==='2'
coercion — .concat()
It is recommended to use +
and +=
which are better/faster over .concat()
for performance reasons.
//automatically convert to string; number + -> concat
console.log(1+"2") //12
console.log(2+"1") //21
console.log(1+2+"1") //31//no concat, will automatically convert to math operation
console.log(5-"2") //3
console.log(5-"a") //NaN - Not a number, invalid operation, no ascii code in JS
coercion comparison
42 == "42"; // true
1 == true; // truevar x = "10";
var y = "9";
x < y; // true
dynamic casting
typeof 42 //number
typeof "abc" //string
typeof Boolean(1) //boolean
let a = 1
console.log(typeof !a) //boolean, check the 2nd value
console.log(!1) //false
let a = 1
console.log(typeof !!a) //true, !! - double negation
let a = 0
console.log(typeof !!a) //false, boolean will be false
!!"" //false, nothing in there
!!" " //true, there is a space, it is not empty
!!{} //true - it is object, empty box but there is somethingnull == false //false
undefined == false //false
null == undefined //true
null === undefined //false
console.log() //undefined|| or
&& and
! not
? optional chaining //if not null or undefined, read the value deep within a chain, or check if function exists, return undefined
?? nullish coalescing, nor //replace || for falsy values, only if null or undefined, then use second value
+ numeric representation
console.log(1 || 0) //1
console.log(1 && 0) //0
console.log(1 || 2 || 3) //1 - OR - looking for the first TRUCY value, otherwise return last element
console.log(1 && 2 && 3) //3 - AND - looking for the first FALSY value, if not any, return last element
Loops — Array
const array = [1, 2, 3, 4, 5];for (let i = 0; i < array.length; i++) {
console.log(array[i]);
}for (const item of array) {
console.log(item);
}
Loops — Object
const obj = { a: 1, b: 2 };Object.entries(obj).forEach(
([key, value]) => console.log(key, value)
);Object.keys(obj).forEach(key => {
console.log("key: ", key);
console.log("Value: ", obj[key]);
} );const keys = Object.keys(obj)
for (const key in obj) {
console.log(key)
}const values = Object.values(obj)
for (const key in obj) {
console.log(obj[key])
}
`use strict` Mode
It gives you less tolerance for errors, put on top of your program
Error Types
ReferenceError
occurs when you try to use a variable that doesn't exist at all.TypeError
occurs when the variable exists, but the operation you're trying to perform is not appropriate for the type of value it containsSyntaxError
occurs when trying to interpret syntactically invalid code.RangeError
occurs when a number "out of range" has occurred.Runtime Error
is an error that occurs during the running of the program, also known as the exceptions - "bugs"Complier Error
occurs before any part of your code runs, the interpreter can not understand at any point in your program,
Error Handling (exception catching)
- when you do something that may break your program
try
lets you test a block of code for errors.catch
lets you handle the error.throw
lets you create custom errors. (execution of the current function will stop, the statements after throw won't be executed, and control will be passed to the first catch block in the call stack. If no catch block exists, the program will terminate.)finally
lets you execute code, after trying and catching, regardless of the result.
JavaScript ES6
New Features of ES6
- let const vs var
- arrow function
- template literal
- default params
- destructuring
- spreading (…) /rest (…rest)
- promises
- class syntax
- async/await(ES7)
var and let/const
- var -issue with value hoisting, put things on the top, scope to the function — old way
- let/const — not accessible before the line we declare them, scope to the block
- let: re-assign value; const: no re-assign allowed, we can not change the pointer
console.log(a); //undefined - variable exist, not able to access to the value
var a = 1;
console.log(b); //ReferenceError - b is not exist, temporal dead zone
let b = 1;
Scoping
for(let i = 0; i < 5; i++){ //let - block scope, let i gives us a new i for each iteration,
setTimeout(()=>{
console.log(i) //0 1 2 3 4
}, 500);
}for(var i = 0; i < 5; i++){ //var - function scope
setTimeout(()=>{
console.log(i) //5 5 5 5 5
}, 500);
}
new j
created each iteration, which gets a copy of the value of i
at this moment
var keeps = [];
for (var i = 0; i < 3; i++) {
let j = i;
keeps[i] = function keepEachJ(){
return j;
};
}
keeps[0](); //0
keeps[1](); //1
keeps[2](); //2
Hoisting
- Variable hoisting means the JavaScript engine moves the variable declarations to the top of the script.
Temporal Dead Zone
- accessing a let or const variable before its declaration (within its scope) causes a ReferenceError.
- between the creation of a variable’s binding and its declaration, is called the temporal dead zone.
Describe Arrow Function
- simple syntax, less code, if within one line, implicitly returned without the use of the return keyword
- does not have its own “this” to be referred to as the current object
- does not need to bind functions
- use lexical scoping — ‘this’ refers to its current surrounding scope and no further.
The disadvantage of Arrow Function
- does not have “arguments” which access all the inputs parameters
- can never be used as constructor functions
- can never be invoked with the new keyword
- a prototype property does not exist for an arrow function.
var obj = {
name: "mic",
getName: function(){
return this.name //"this" belongs to the obj that calls the function
}
getName2: () => {
return this.name //arrow function does not have its own "this", "this" here means the Window
}
};
console.log(obj.getName()) //mic
console.log(obj.getName2()) //undefined
default params
const genParam = () => {
console.log("Called");
return 5;
};function f(x, y = genParam()) {
console.log(x, y);
}f(1); //1, 5
f(1, 9); //1, 9
f(1, undefined); //1, 5 - no difference from f(1);
f(1, null); //1, null - null considered to be a valid input
destructuring
const obj = { x: 1 };
const { x: otherX } = obj; //re-naming, will be stored in the new name
console.log(otherX) //1
//console.log(x) //no good, referenceError, x is not defined.//in array, order matters
const arr = [1, 2];
let [z, q] = arr;
console.log(q, z); //2 1
[q, z] = [z, q];
console.log(q, z); //1 2 - reassign the value//Re-naming
const obj = { x: 1 };
const { x: newVariable } = obj; //just about the syntax, change the name x to newVariable
// const newVariable = obj.x //x as the key to get the value and stores in the variable
console.log(newVariable) //1
spreading (…)
const obj = { x: 1 };
const newObj = { ...obj };
console.log(newObj) //{ x: 1 }
console.log(obj === newObj) //false - shallow cloneconst newObj = { ...obj, y: 2 }; // addition
const newObj = { ...obj, x: 2 }; // overwriteconst s = "Hello";
const sArr = [...s];
console.log(sArr) //["H", "e", "l", "l", "o"]
console.log(sArr.length); //5
rest (…rest)
function func(a) {
console.log(a); //1
}
func(1);function func(a, ...rest) { //rest element must be the last parameter
console.log(rest); //[2, 3, 4, 5, 6, 7]
}
func(1, 2, 3, 4, 5, 6, 7);function func(a, b, ...rest) {
console.log(rest); //[3, 4, 5, 6, 7]
console.log(arguments[0]); //1 - arguments is for everything in the params, it returns array like object, but does not carry any array methods
}
func(1, 2, 3, 4, 5, 6, 7);
Promise
sync vs async
- sync code, code that is going to execute right away, one expression at a time.
- async code, will not get executed right away, but sometime in the future, the next expression runs while the previous finishes up.
Asynchronous JS
- JS uses callback, promise, and async-await to implement asynchronous patterns.
Promise(event loop, task scheduling)
- JS is a single-threaded language, that uses a promise to handle async operations, and avoid callback hell which is a chained nested code
- A Promise is a proxy for a value not necessarily known when the promise is created, which represents WORK that needs to be done at some point.
3 phrases -> pending, fulfilled, rejected
- chain .then() to do something, and/or .catch() to catch error
- we can chain more .then(), return a promise, and execute only after the main thread is done
const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));let promise = new Promise((resolve , reject) => {
fetch("https://myAPI")
.then((res) => {
// successfully got data
resolve(res);
})
.catch((err) => {
// an error occured
reject(err);
});
});const promise = new Promise((resolve, reject) => {
//do a thing, possibly async, then...
if(/* everything turned out fine */){
resolve("Stuff worked!")
} else {
reject(Error("It broke!"))
}
});promise.then((resolve) => { console.log(resolve) }); //Stuff worked!const myPromise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('foo');
}, 300);
});myPromise.then((resolve) => {
console.log(resolve);
}).catch((err) => {
console.log(err);
})
Promoise.all([])
- run promises in parallel, create an array of promises, and then use
Promise.all(promisesArray)
. - send all promises, returns a single Promise that resolves to an array of the results of the input promises
- will reject immediately upon any of the input promises rejecting => if one fails, all fail
Promise.racce()
works similar asPromoise.all()
but return a single value whichever returns the first
const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'foo');
});Promise.all([promise1, promise2, promise3]).then((values) => {
console.log(values); //return an array - [3, 42, "foo"] that contains all the value
});
async/await
- cleaner style handling asynchronous tasks, return a promise, await takes a pause, then returns its result -> must with the async keyword
- easier for promise chaining (not faster), better for accessing the value in the scope with assigned variable, and the output of function2 is dependent on the output of function1
- start with async function, replace .then() with await, use try catch for error handling, the async function can be dynamic like MongoDB update/delete endpoints
main thread (console.log) > micro (promise, async/await-pauses) > macro (timeout, interval)
Function
Parameters vs Arguments
function counter(x, y){ //x & y are parameter console.log(x+y);
//do something
}
var a = 10, b = 2;
counter(a, b); //a & b are argument
Callbacks
- a function passed into another function as an argument, and run after another function has finished.
- this function will be executed later only after this other function has finished executing
- JS is a synchronous single-threaded language, but with callback functions, we can perform the async task.
- great for async calls, handle something after something else has been completed, enforce the order of operation that we want (e.g., Event listeners make use of this)
function greeting(name) {
alert('Hello ' + name);
}
function processUserInput(callback) {
var name = prompt('Please enter your name.');
callback(name);
}
processUserInput(greeting);
First-class function
- In JS, is called first-class function, aka, first-class citizens.
- The callback is also a first-class function going by this definition because it is also passed as an argument.
- The ability of functions to be:
- Assigned to variable
- Passed as an argument to another function
- Returned from another function
Unary Function
- a function that accepts exactly one argument. It stands for a single argument accepted by a function.
const unaryFunction = a => console.log (a + 10); // Add 10 to the given argument and display the value
First Order Function
- a function that doesn’t accept another function as an argument and doesn’t return a function as its return value.
const firstOrder = () => console.log ('I am a first order function!');
Higher Order Function
- a function that accepts another function as an argument or returns a function as a return value or both.
- map, reduce, filter, find, etc
const firstOrderFunc = () => console.log ('Hello, I am a First order function');
const higherOrder = (ReturnFirstOrderFunc) => ReturnFirstOrderFunc();
higherOrder(firstOrderFunc);
Currying
- a nested function that takes multiple arguments one at a time, the last function returns the result based on all the argument
- convert a function with multiple arguments into several functions of a single argument in sequence.
- we do not change the functionality of a function, we just change the way it is invoked
- useful in the context of function composition
const addNumber = (a) => (b) => (c) => a+b+c;
Composition
- a function called in another function
- You can chain higher-order functions into a composition
Page Redirection
window.open('{url}','_blank');
- open in a new windowfunction redirect() { window.location='{URL}' };
setTimeout(redirect, 1000);
Popup Boxes
- alert:
window.alert("sometext");
- prompt:
window.prompt("sometext","defaultText");
- confirm:
window.confirm("somtext")
ok and cancel -> return boolean
Function Declaration vs Expression
- Function declarations load before any code is executed
- Similar to the var statement, function declarations are hoisted to the top of other code.
- Function expressions load only when the interpreter reaches that line of code.
- Function expressions aren’t hoisted, which allows them to retain a copy of the local variables from the scope where they were defined.
myF()
function myF(){
console.log("My Function") //it is okay to under the function declaration using keyword function
}
myF()
const myF = function(){
console.log("My Function") //ReferenceError, no good with function expression, can not access function initialization, myF() failed directly
}
myF()
var myF = function(){
console.log("My Function") //TypeError, no good, myF is not a function, var myF = undefined;
//when you try to execute myF, it is good, it is there, but it triggers the function which is undefined, that it breaks the rule.
}
Benefits of Function Expressions:
- As closures
- As arguments to other functions
- As Immediately Invoked Function Expressions (IIFE)
IIFE — immediate invoked function expression
- runs as soon as it is defined, invoke immediately
- variables declared in the function expression will not be available outside the function
- primary purpose: data privacy because any variables declared within the IIFE cannot be accessed by the outside world.
Contains two major parts:
- function expression within the Grouping Operator ()
- immediately invoke the function ()
Examples
(function() { /* */ })()
(() => { /* */ })()
for (var i = 1; i <= 3; i++) { //var makes i stays in the function scope
(function(index) { //inner function gets local copy of outer function arguement
setTimeout(function() { alert(index); }, i * 1000); //having a copy of i in it
})(i); //using a self-invoking function, IIFE, each iteration created a new scope for each iteration
}
Scope
lexical & global
- Lexical Scope: a variable defined outside a function can be accessible inside another function defined after the variable declaration (Whenever execution context is created, a lexical environment is also created)
- Global Scope: contains, and is visible in, all other scopes (Global object (in browsers, it is called window) which is referenced by ‘this’.)
block vs function
var
is function scope.- Function scope is within the function, each function creates a new scope
let
andconst
are block scope.- Block scope is within curly brackets, variables declared inside a { } block cannot be accessed from outside the block
“The Principle of Least Privilege” (POLP)
- encourages us to use a block (and function) scoping to limit the scope exposure of variables.
- the least exposure helps keep code understandable and maintainable and helps avoid many scoping pitfalls.
Scoping pitfalls
- name collision: the identifier comes from one shared scope (like the global scope)
- unexpected behavior: expose variables/functions whose usage is otherwise private to a piece of the program
- unintended dependency: expose variables/functions unnecessarily which invites other developers to use and depend on those otherwise private pieces
Shadowing
- Shadowing: a variable is declared in a certain scope having the same name defined on its outer scope
var a = 99;
{
let a = 10; //let is block scope
let b = 11;
const c = 200;
console.log(a); //10
}
console.log(a); //99
illegal shadowing
- Illegal Shadowing: we can shadow var variable by let variable, but cannot shadow let variable by var variable
let a = 10;
{
var a = 20; //SyntaxError: Identifier 'a' has already been declared
}
//var is not scoped to the block it's in, it's scoped to the containing function.
//if there is no containing function, it ends up in the global scope.
//it conflicts with the let a declaration which is also in the global scope.
Closure
what is closure
- a function returned by another function that still has access to its outer scope variable
- ability to remember and access scope even if was called from another scope
- used to enable data privacy, but cons -> may cause memory leak
function makeCounter(){
let count = 0; //private variable for keeping data private and safe
//value by the function will be saved as it will be needed by the inner function, not for garbage collection
return function(){
count++
return count;
};
}
const counterFunc = makeCounter();
console.log(counterFunc()); //1
console.log(counterFunc()); //2
const newFunc = makeCounter(); //a new function, variabel value start over
console.log(newFunc()); //1
console.log(newFunc()); //2
Closure Definition
- The closure is observed when a function uses variable(s) from outer scope(s) even while running in a scope where those variable(s) wouldn’t be accessible.
- Closure encapsulates the body of code together with the lexical scope.
- The closure is most common when working with asynchronous code, such as with callbacks.
Closure Requirements
- Must be a function involved
- Must reference at least one variable from an outer scope
- Must be invoked in a different branch of the scope chain from the variable(s)
Disadvantages of Closures
The closure is associated directly with memory consumption. Hence, it leads to high consumption of memory, if a lot of closures are created, since the allocated memory is not garbage collected till the program expires.
Garbage Collector
It is a program in the browser/JS Engine that is responsible for freeing up the memory which is unutilized.
This
“this” keyword
- entirely dependent on how it is called
- refers to the object that the function is a property of.
- the value will always depend on the object that is invoking the function.
bind vs apply vs call
bind()
- The bind() method creates a new function used to provide a proper “this” refers to the function
- it returns a new bound function, does not call the function, but refers to it so that you can execute it later
apply()/call()
- same, just the different way to put in the parameter: call => comma; apply => array
- directly triggers itself, call it right now, unlike bind, not yet to call
this in functions
- “this” behaves differently in arrow functions compared to a regular function.
this
inregular function
,this
belongs to function, it binds its own value, like the person.fullName(), "this" refers to the left to the '.'this
inarrow function
,this
DOES NOT belong to arrFunc, it is outside of the function, arrow functions don't bind their own this value
this examples
//1. this IN method, this -> object owner
const person = {
firstName: 'Viggo',
lastName: 'Mortensen',
fullName: function () {
return `${this.firstName} ${this.lastName}` //just like ${person.firstName} ${person.lastName}, this -> object owner
},
// fullName: () => { //"this" has nothing to do with the scope where the function is created, it has to do with how the function is executed
// console.log(this); // "this" refers to Window, if we do person.fullName() which means Window.fullName() - it will be undefined
// return `${this.firstName} ${this.lastName}`
// }, //when we are using arrow function, "this" will be jumping out to the original block which will be global scope
shoutName: function () {
setTimeout(() => {
//keyword 'this' in arrow functions refers to the value of 'this' when the function is created
console.log(this); //"this" refers to the person Object
console.log(this.fullName())
}, 3000)
// shoutName: function () {
// setTimeout(function () => { //we have to use arrow function here instead
// console.log(this); // "this" refers to Window object here
// console.log(this.fullName()) //this.fullName is not a function - it has to do with the execution context
// }, 3000)
}
}
person.fullName() //"this" refers to the left to the '.' here is the person
//2. this IN function, this -> global on browser -> Window
function a() {
console.log(this) //Window, this -> global
}
a() //Window
console.log(this) //Window//2.1 this IN function, strick mode, this -> undefined
function a() {
"use strict"
console.log(this)
}
a(); //undefined
//3. this IN event, this -> HTML element that received the event
<button onClick="this.style.display"="none">
click to remove me!
</button>
DOM
Critical Rendering Path — steps the browser makes to paint the page.
- DOM — browser compiles the Document Object Model;
- CSSOM — browser compiles the CSS Object Model;
- Render Tree — browser combines DOM and CSSOM to render the tree;
- Layout — browser computes the size and position of each of the objects;
- Paint — browser converts the tree into the pixels on the screen;
Optimize CRP
- Optimize the order of sources — load critical resources as soon as possible;
- Minimize the number of sources — reduce the number, load async;
DOM Elements
- selector: getElementById, getElementByTagName, querySelector, querySelectorAll;
- navigation: children (elements): childNodes (nodes), firstElementChild, lastElementChild, parentElement, previousElementSibling, nextElementSibling;
- attributes: classList, clientHeight, clientWidth, childElementCount, setAttribute(attrName, value) removeAttribute(attrName) removeAttribute(attrName) ;
DOM Manipulation
- Adding Elements:
body.append("Hello World")
;body.append(div)
;.appendChild(div)
; - Creating Elements:
document.createElement("div")
; - Modifying Element Text:
div.innerText="Hello World"
(how HTML works and looks at CSS like invisible);div.textContent="Hello World"
(exact text content copy pasted in HTML); - Modifying Element HTML:
div.innerHTML="<strong>Hello World</strong>"
; - Removing Elements:
div.remove()
;.removeChild(span)
; - Modifying Element Attributes:
span.getAttribute("id")
;span.setAttribute("title", "Hello")
;span.removeAttribute("id")
; - Modifying Data Attributes:
console.log(span.dataset.test)
;span.dataset.newName="hi"
; - Modifying Element Classes:
span.classList.add("new-class")
;span.classList.remove("hi")
;span.classList.toggle("hi2", false)
; - Modifying Element Style:
span.style.backgroundColor="red"
; - Select:
document.querySelector("div")
; - Make Interactive:
btn.addEventListener()
; - Change CSS: Style Property
Event
- defined action or occurrence
- we can write code that runs specifically when one of these actions or occurrences happen
Event Handling
- Events are triggered by the user, the browser, or something else to make changes to HTML, CSS, and JavaScript
- Detect and Respond to an event using JavaScript
- identify a DOM node to monitor
- identify the event you want to respond to
- create a function to run when the event is triggered
addEventListener()
element.addEventListener(event, function, useCapture)
- addEventListener() method attaches an event handler to the specified element, anonymous functions are not supported in the AddEventListener call, which will cause an error.
- removeEventListener() method to remove an event handler that has been attached to the addEventListener() method.
- event: A String that specifies the name of the event like “click”, “mouseover”, “keyup”
- function: Specifies the function to run when the event occurs
- useCapture: Optional. A Boolean specifies whether the event should be executed in the capturing (true) or bubbling phase (false).
DOMContentLoaded
- run the callback function when DOM loads
document.addEventListener('DOMContentLoaded', (event) => {
console.log('DOM fully loaded and parsed');
});
Event.preventDefault()
- prevent the browser from executing the default action of the selected element.
- Clicking on a “Submit” button, prevent it from submitting a form
- Clicking on a link, prevent the link from following the URL
Event.stopPropagation()
- prevents further propagation of the current event in the capturing and bubbling phases.
- By default, events are bubbled. This propagation of event is expensive and we can stop it by calling, the .stopPropagation() method.
Event Propagation
- like a deeper ocean goes to the layer one by one travels through the DOM tree to arrive at its target and what happens to it afterward
Three phases in order are:
- the
event capturing phase
- top to the bottom - outermost to inner - click outer which will trigger the inner one. - the
target phase
- all the listeners registered on the event target will be invoked - the
event bubbling phase
- bottom to the top - innermost to outer - click the inner one, the outer one will also be clicked
Event Delegation
- Allow you to avoid adding event listeners to specific nodes; instead, the event listener is added to one parent.
- Instead of attaching the event listeners directly to the buttons, you delegate listening to the parent
<div id="buttons">
. - When a button is clicked, the listener of the parent element analyzes the bubbling event and catches on a matched child element (recall the event propagation).
PROS of event delegation:
- Improves Memory
- Write less code
- DOM manipulation
CONS of event delegation:
- All events are not bubbled up, like, blur, resize, etc.