JavaScript - robbiehume/CS-Notes GitHub Wiki
- Different ways to loop through array in JS
- JS vs jQuery vs Ajax
- JavaScript Overview (MDN)
- Complete JavaScript handbook (free codecamp)
- Demystifying JS Promises and
async/await
- Prototypes / classes: link
- OOP: link
- Promises: Demystifying JS Promises and
async/await - Default / named imports and exports: link
- All numbers are stored as the
Numbertype, which are represented as floating point numbers- For example
1 === 1.0istrue
- For example
- Semicolons are technically only needed before a line that starts with
[,(,/,+,-or IIFEs
let vs var: link
- In modern JS, always use
letovervar - Other link showing loop use case
- The main difference is scoping rules. Variables declared by
varkeyword are scoped to the immediate function body (hence the function scope) whileletvariables are scoped to the immediate enclosing block denoted by{ }(hence the block scope)
-
||and??operator in variable definition: link; link2-
||(logical OR):- Returns the first truthy value
- If the left-hand side is falsy (like
0,'',false,null, orundefined), it returns the right-hand side - Ex:
const value = '' || 'default'// Since '' is falsy, it outputs 'default'
-
??(nullish coalescing):- Returns the first non-null / undefined value
- Only
nullandundefinedtrigger the fallback to the right-hand side - Ex:
const value = '' ?? 'default'// Since '' is neither null or undefined, it outputs '', even though it's falsy
-
&&: - The main difference is that
||will treat all falsy values as if they need the fallback, whereas??only applies the fallback if the value isnullorundefined; it is more restrictive
-
- Ternary operator:
(test condition) ? expression1 : expression2;
- Primitive data types:
Number,String,Boolean,null,undefined - Non-primitive (reference) data types:
Object,Array,Function - Get data type:
typeof <var name or value>(e.g.typeof 42)
-
String(42); // "42" (42).toString(); // "42" true.toString(); // "true"
- β
Explicit Conversion (Recommended)
Number("42"); // 42 parseInt("42"); // 42 (Removes decimals) parseFloat("42.5"); // 42.5
- β Watch out for invalid conversions
Number("hello"); // NaN parseInt("42px"); // 42 (Stops at first non-number character)
-
Boolean(0); // false Boolean(1); // true Boolean(""); // false Boolean("hello"); // true
-
// convert to String JSON.stringify({ name: "Alice" }); // '{"name":"Alice"}' // convert from JSON to Object JSON.parse('{"name":"Alice"}'); // { name: "Alice" }
-
// convert to String [1, 2, 3].toString(); // "1,2,3" [1, 2, 3].join('-'); // "1-2-3" // convert to Number (caution with Implicit Conversion) [10] * 2; // 20 (Array coerced to number) [10, 20] * 2; // NaN (Multiple elements cause failure)
- NOTE: strings cannot be modified directly, but they can be reassigned with a whole new value
- String literal to embed variables:
let msg = `Hi ${name}, it's ${time} o'clock!` - Access a character:
str[index]orstr.charAt(index)- Difference is what happens when you access a non-existing string index:
- With
[]you get anundefined - With
charAt()you get an empty string
- With
- Difference is what happens when you access a non-existing string index:
- Just like with arrays, you can loop through strings:
-
forloop orfor...ofloop - Ex:
for (let char of strName) { console.log(char); }
-
-
.slice():-
strName.slice(startIndex, stopIndex): get slice from start to stop index-
startIndexis inclusive,stopIndexis exclusive, just like python - Ex:
'hi there'.slice(0, 2)β'hi'
-
-
strName.slice(startIndex): get slice from start index to the end-
startIndexcan be positive (count from beginning) or negative (count from the end) - Ex:
'hello'.slice(3)β'lo';'hello'.slice(-2)β'lo'
-
- It doesn't modify the original, but returns a new one
-
-
.concat(): works similar to+- It can take any number of arguments
- Ex:
firstName.concat(lastName)
-
.toUpperCase(): change all letters to up percase -
.toLowerCase(): change all letters to lowercase -
.trim(): remove unwanted spaces from both ends of the string- Ex:
' hello '.trim()β'hello'
- Ex:
-
.replace(): replace a substring with another- Ex:
'hello there'.replace('hello', 'hi')β'hi there' -
NOTE: the
replace()method only changes the first occurrence, usereplaceAll()to change all
- Ex:
-
.split(): divide a string into an array of substrings based on a specified separator
-
Feature JavaScript Object JSON Quotes around keys Optional (except special cases) Mandatory (must use "only)Data type flexibility Can store functions, undefinedOnly supports strings, numbers, null, arrays, and objectsExecution Can contain executable code Pure data format, no code execution Comments allowed? β Yes β No (JSON does not support comments)
-
Convert JSON string to object:
JSON.parse('{ "name": "John", "age": 22 }')β{ name: "John", age: 22 } -
Convert object to JSON string:
JSON.stringify({ name: "John", age: 22 })β{"name": "John", "age": 22}
- Can access values with
.:objectName.keyName- Can also do brackets (
[]):objectName['keyName'] - When the key is a variable, it's best to use brackets:
objectName[keyVar]
- Can also do brackets (
- Can do both
.and[]notations for setting/updating values as well:-
objectName.keyName = valueorobjectName['keyName'] = value
-
-
Same for deleting:
delete objectName.keyNameordelete objectName['keyName'] -
Get all keys from an object:
Object.keys(objectName)// returns an array -
Get all values from an object:
Object.values(objectName)// returns an array -
Get all key-value pairs from an object:
entries(objectName)// returns an array of arrays (key is first element, value is second) -
Get length (# of keys) of an object:
Object.keys(objectName).length- Objects donβt have a
.length, need to get length of keys array
- Objects donβt have a
- Objects can also have methods:
-
const book = { title: 'The Hobbit', author: 'J.R.R. Tolkien', year: 1937, genres: ['Fantasy', 'Adventure', 'Classic'] displayInfo: function () { console.log(this.title + ' by ' + this.author); } }; book.displayInfo(); // 'The Hobbit by J.R.R. Tolkien'
-
- Can do something similar to python
.get()const d = { a: 1, b: 2 }; const val = d['c'] ?? 'default_val`; // outputs: 'default_val'
-
Add element to end of array:
arr.push(val) -
Remove element from end of array:
arr.pop(val) -
Check if a value is in an array:
arr.includes(val) -
Flatten multi-dimensional array:
[].concat(...arr) - Looping through array: link (forEach, for(i in arr), for(x of arr), etc.)
-
.filter()/.map()/.reduce()-
const numbers = [1, 2, 3, 4]; const evenNumbers = numbers.filter(num => num % 2 === 0); // [2,4] const doubled = numbers.map(num => num * 2); // [2, 4, 6, 8] const sum = numbers.reduce((acc, num) => acc + num, 0); // 10
- Can also use
reduce()to find an object in the array with a certain value criteria- Ex. find the oldest person object:
people.reduce((max, person) => person.age > max.age ? person : max)
- Ex. find the oldest person object:
-
- JavaScript doesn't have a built-in
sum()function, but it can be accomplished with.reduce():-
const numbers = [1, 2, 3]; const sum = numbers.reduce((acc, num) => acc + num, 0); // 1 + 2 + 3 = 6
-
-
.forEach(): link -
.some()/.every()/.find(): link-
some()/every()is equivalent toany()/all()in Python-
some():const containsPositive = numbers.some(num => num > 0)// true for numbers = [-1,2,3], false for [-1,-2] -
every():const containsAllPositive = numbers.every(num => num > 0)// true for numbers = [1,2,3], false for [-1,2,3]
-
-
find()is the same syntax assome()/every(), except instead of a boolean it returns the object- Ex:
firstPositiveNumber = numbers.find(num => num > 0)// 1 for [1,2,3]
- Ex:
-
-
.map():arrayName.map(<function>)- Transforms each element in an array (e.g., increasing every item price by $20)
- It doesn't modify the original array in-place and instead returns a new, modified array
- It's a cleaner and more efficient than doing a for loop through every element
- It's called on the array and takes in a function (pre-defined or arrow function)
- Ex: apply $20 discount to all prices
const prices = [200, 300, 400]; const discountedPrices = prices.map(p => p - 20)
-
.filter():arrayName.filter(<function>)- Selects only the elements that pass a certain condition (e.g., keeping only item prices greater than $250)
- Like
.map(), it creates and returns a new array - Ex: find even numbers
const numbers = [1, 4, 7, 10, 9, 2]; const filteredNumbers = numbers.filter(n => n % 2 == 0) // [4, 10, 2]
-
.reduce():arrayName.reduce(<function>, initialValue)- Processes the entire array to produce a single value (e.g., calculating the total price of all items)
- Ex: get product of integer array
const numbers = [1, 2, 3, 4, 5]; const total = numbers.reduce((acc, price) => acc * price, 1) // 120
-
Python for i in xJavaScript Equivalent for i in x:β for (const i of x) {}for i in range(len(x)):β for (let i = 0; i < x.length; i++) {}for i, v in enumerate(x):β x.forEach((v, i) => console.log(i, v))
-
for (let i = 0; i < 10; i++) { console.log(i); }
-
const x = [10, 20, 30]; for (const i of x) { console.log(i); }
- Works on arrays, strings, Maps, Sets, and other iterables
- Best choice for iterating over values
-
for (const i in x) { // can do let instead of const console.log(x[i]); // Access value using index }
- Iterates over keys (indexes for arrays, property names for objects)
-
x.forEach(i => console.log(i));
- Uses a
callback functionto iterate over elements - Good for clean, functional-style loops
- Normal
whileloopwhile (x < 10) { i -= 1 }
-
do...whileloopdo { // statement(s) } while (boolean_expression);
- Syntax errors can't be handled
- The only way to fix a syntax error is to correct the mistake before running the program
- Runtime errors (exceptions) can be handled
- You can prevent your program from crashing when a runtime error occurs by using error-handling techniques, which allow the program to continue running despite the error
- Ex:
try { // Code that might cause an exception } catch (error) { // Code to handle the exception } finally { // Code that always runs }
- The
errorvariable (commonly namederrororerr) holds an object with details about the exception- This helps with diagnosing and handling issues effectively
- The error object typically has these properties:
-
name: Specifies the type of exception that occurred. -
message: Provides a description of what went wrong, offering helpful context about the issue.
-
-
Note: When an exception is encountered inside the
tryblock, the program immediately exits thetryblock and jumps to thecatchblock- This means any code after the exception-causing code in the
tryblock does not execute
- This means any code after the exception-causing code in the
- The
finallyblock always runs, regardless of whether an exception occurs -
Note: you must use either the
catchorfinallystatement after atryblock
- The
throwstatement allows you to generate a custom error that crashes the program- It helps you detect an error before it happens, so you an customize the error
- You can use
throwwith or withouttry...catch - Throwing a custom error looks like:
throw new Error('custom error message') - Ex:
const numerator = 5; const denominator = 0; try { if (denominator === 0) { // Check if denominator is 0 throw new Error("Cannot divide by zero"); // Generate error } console.log(numerator / denominator); } catch (error) { console.log(`Error caught: ${error.message}`); }
- Demystifying JS Promises and
async/await - Synchronous vs Asynchronous JavaScript β Call Stack, Promises, and More
- JavaScript is synchronous by default, but it is possible to asynchronous tasks
- We can classify most asynchronous JavaScript operations with two primary triggers:
-
Browser API/Web API events or functions: these include methods like
setTimeout, or event handlers like click, mouse over, scroll, and many more - Promises: unique JavaScript object that allows us to perform asynchronous operations
-
Browser API/Web API events or functions: these include methods like
- Browser APIs like
setTimeoutand event handlers rely oncallbackfunctions. A callback function executes when an asynchronous operation completes - The event loop manages the call stack and receives browser APIs / callback function calls
- There is also a special
callback queue/task queuethat holds tasks to run once the call stack is empty - For promises, the JavaScript engine doesn't use the
callback queue; it uses another special queue called thejob queue - Items in the
job queueare given priority over thecallback queueitems - Order of precedence (first to last): call stack, promises, browser APIs (async callbacks)
-
-
setTimeout(): run a function after a specific delaysetTimeout(function, milliseconds)
-
setInterval(): run a function repeatedly on an intervalsetInterval(function, milliseconds)
-
clearInterval(): stop the execution of a running interval-
let intervalID = setInterval(function() { console.log('This message repeats every 3 seconds.'); }, 3000); // 3000 milliseconds = 3 seconds setTimeout(() => { clearInterval(intervalID); }, 10000);
-
fetch()
- A callback is a function passed into another function as an argument. It is executed after some operation has been completed
- Ex:
function readFile(callback) { setTimeout(() => { console.log("Reading file..."); const fileContent = "Hello, this is the file content!"; callback(fileContent); }, 1000); } function processFileContent(content) { console.log("Processing file content to uppercase:"); console.log(content.toUpperCase()); } // Reading file and then processing its content readFile(processFileContent);
-
Callback Hell
- Callbacks are simple when it's a small amount, but when it's a large number it creates ugly nesting
- The modern solution is to use Promises instead
- Guide to JS promises (free code camp)
- In JavaScript, promises are special objects that help you perform asynchronous operations
- You can create a promise using the
Promiseconstructor, which takes anexecutorfunction - In the executor function, you define what you want to do when a promise returns successfully or when it throws an error
- You can do that by calling the
resolveandrejectmethods, respectively
- You can do that by calling the
- Example:
const promise = new Promise((resolve, reject) => resolve('I am a resolved promise')) - After the promise is executed, we can handle the result using the
.then()method and any errors with the.catch()methodpromise.then(result => console.log(result))
- For promises, the JavaScript engine doesn't use the same
callback queuewe have seen earlier for browser APIs. It uses another special queue called thejob queue - Can chain
.then()where it will pass the return value onto the next- Ex:
fetch(url) .then(response => response.json()) .then(data => console.log(data));
- Ex:
- Every time a promise occurs in the code, the executor function gets into the job queue
- The event loop works, as usual, to look into the queues but gives priority to the
job queueitems over thecallback queueitems when the stack is free - The item in the callback queue is called a
macro task, whereas the item in the job queue is called amicro task
-
awaitis usually used to unwrap promises by passing aPromiseas the expression - Using
awaitpauses the execution of its surroundingasyncfunction until the promise is settled (that is, fulfilled or rejected)
- jQuery Examples
- The standalone $ stands for jQuery. Ex:
$.each()is equivalent tojQuery.each()- Side note:
$.each()is not the same as$(selector).each(); source
- Side note:
- There is also the $() method used to get HTML elements
-
$("p"): get all <p> elements -
$("#test"): get all elements with id="test" -
$(".test"): get all elements with class="test" -
$(this): get the current HTML element
-
-
Understanding
thisis crucial for working with object methods, event handlers, and callbacks in JavaScript- The
bindmethod can be used to ensurethishas the correct context
- The
- The value of
thisis determined by how a function is called, not where it is defined (except for arrow functions)- For arrow functions, the value of
thisis determined by where it's defined - Example:
-
const user = { name: 'Alice', greet() { console.log(`Hello, ${this.name}`); } }; user.greet(); // "Hello, Alice" const greet = user.greet; greet(); // "Hello, undefined" (or "Hello, window.name" in non-strict mode)
-
- For arrow functions, the value of
- You can explicitly bind
thisusingbind,call, orapply-
const greet = user.greet.bind(user); greet(); // "Hello, Alice"
-
-
thisin different contexts:- In the global execution context (outside of any function), this refers to the global object
- When a method is called as a property of an object, this refers to the object itself
- In event handlers, this typically refers to the element that fired the event, but in modern JavaScript frameworks, this behavior can be customized
- Deciding how you want use
thisdepends on which type you use-
Traditional function: the value of
thisdepends on how the function is called-
function Person() { this.age = 0; setInterval(function() { this.age++; // `this` refers to the global object, not the `Person` instance console.log(this.age); }, 1000); } const p = new Person(); // `this` inside setInterval does not refer to `p`
-
-
Arrow function: the value of this is inherited from the outer scope (lexical binding)
-
function Person() { this.age = 0; setInterval(() => { this.age++; // `this` refers to the instance of `Person` console.log(this.age); }, 1000); } const p = new Person(); // `this` inside the arrow function refers to `p`
-
-
Traditional function: the value of
- An anonymous function is simply a function without a name
- They're often used in places where a function is passed as an argument to another function, such as callbacks, event handler, or function expressions
- Example:
-
setTimeout(function() { console.log('This is an anonymous function'); }, 1000);
-
-
Feature Function Declaration Function Expression Arrow Function Syntax function name() {}const name = function() {}const name = () => {}Hoisting Yes (can be called before defined) No (must be defined before use) No (must be defined before use) thisBindingHas its own thisHas its own thisInherits thisfrom the surrounding scopeAnonymous No (must have a name) Can be anonymous or named Always anonymous argumentsobjectYes Yes No (use rest parameters ...argsinstead)Pros Clear, named functions, hoisted More flexible (anonymous/named), context-aware Concise syntax, easy handling of this, good for callbacksCons Verbose for simple functions, thiscan be confusingNot hoisted, thisbinding can be trickyLack of own thisandarguments, less readable for complex logicUse Cases Named functions, reusable logic Callback functions, passing functions around Short callbacks, avoiding thisissues
- The most common way to define a function; uses the
functionkeyword - Hoisting: function declarations are hoisted to the top of their scope. This means you can call the function before it is defined in the code
- When to use function declarations:
- Top-level functions: when you need functions that should be available throughout a scope (e.g., utility functions)
- Consistency: function declarations are straightforward and consistent, making your code easier to read and understand
- Involves creating a function and assigning it to a variable. It can be anonymous or named
- Example
-
// Anonymous function expression; for named, there would be a space and a function name after the function keyword const greet = function(name) { return `Hello, ${name}!`; };
-
- Hoisting: func. expressions are not hoisted like func. declarations. The variable that holds the function is hoisted, but its value (the function itself) is not. This means you cannot call the function before it is defined
- When to use functional expression:
-
Callbacks: function expressions are often used as arguments in higher-order functions like
setTimeout,map,filter, etc. - Closures: when you need a function that retains access to its surrounding (lexical) scope
- Conditional definitions: if you need to define a function based on certain conditions
-
IIFE: Function expressions are commonly used in Immediately Invoked Function Expressions (IIFE), which execute immediately after they are defined:
-
(function() { console.log('This function runs immediately!'); })();
-
-
Callbacks: function expressions are often used as arguments in higher-order functions like
- Gets access to
...argsobject, which is a syntax that allows you to represent an indefinite number of arguments as an array, similar to*argsunpacking in python -
Note: it's considered good practice to use
constoverletfor declaring arrow functions so that once an arrow function is defined, it can't be reassigned
- When to use which:
- Use
...args: always prefer...argsin modern JavaScript because itβs more flexible and works seamlessly with arrays and arrow functions. - Use
arguments: only in legacy codebases where ES6 features arenβt available, or for specific reasons like accessing arguments in a non-rest-parameter function.
- Use
-
function showArguments() { console.log(arguments); } showArguments(1, 2, 3); // Outputs: { 0: 1, 1: 2, 2: 3 }
- Similar to
*argsunpacking in python, but in JS it's an array instead of tuple -
function showArguments(...args) { console.log(args); } showArguments(1, 2, 3); // Outputs: [1, 2, 3]
-
Destructuring: allows you to unpack values from arrays or properties from objects into distinct variables
-
const person = { name: 'John', age: 30, city: 'Nashville', job: 'Developer' }; const { name, age } = person; // name = 'John'; age = 30 const { name, age, ...otherInfo } = person; // name = 'John'; age = 30; otherInfo = {city: 'Nashville', job: 'Developer'} const fruits = ['apple', 'banana', 'kiwi']; let [fruit1, fruit2, fruit3] = fruits; // fruit1 = 'apple', fruit2 = 'banana', fruit3 = 'kiwi'
-
-
Spread Operator (
...): equivalent to Python unpacking- Purpose: expands (spreads) the elements of an array, object, or iterable into individual elements
-
Used in: array/object creation, function arguments, or combining structures
- Also useful for creating a shallow copy of an array:
[...arr]- This will create a new array with the same primitive values as `arr
- But if the array contains objects or nested arrays, the inner values are still references, not copies
- Also useful for creating a shallow copy of an array:
-
const nums = [1, 2, 3]; const [first, ...last] = nums; // first = 1; last = [2, 3] const newNums = [...nums, 4, 5] // spreads elements of 'nums' into a new array; outputs [1,2,3,4,5] const sum = (a, b, c) => a + b + c; sum(...nums) // can also be passed as function arguments; outputs 6
-
Rest Parameters (
...): equivalent to Python*argspacking- Purpose: gathers (collects) multiple arguments into a single array
- Used in: function parameter lists to handle an indefinite number of arguments
-
function sumAll(...numbers) { return numbers.reduce((acc, num) => acc + num, 0); } console.log(sumAll(1, 2, 3, 4)); // Outputs: 10
-
Key differences:
- Spread: expands elements or values out of an iterable (e.g., array β individual items)
- Rest: collects multiple elements or values into a single array (e.g., multiple arguments β array)
- They are similar to how
*argsis handled in Python for (un)packing depending on if it's in the function definition or call
- Modules allow you to split your code into reusable pieces
-
// utils.js export function add(a, b) { return a + b; } // main.js import { add } from './utils'; console.log(add(2, 3)); // 5
-
-
Named Exports: You can export multiple values from a module
-
export const name = 'Alice'; export function greet() { console.log('Hello!'); }
-
-
Default Exports: Each module can have one default export, which can be imported without curly braces
-
export default function() { console.log('Default Export'); }
-
- Can use localStorage to persist data in the browser. Useful instead of cookies because they last longer
- Allows you to safely access deeply nested properties of an object without having to check each level for null or undefined
- Works on multiple types:
obj?.property;obj?.[expression];obj?.method() - Example:
-
let user = {}; console.log(user.profile.name); // throws an error: Cannot read property 'name' of undefined console.log(user.profile?.name); // returns undefined, no error
-
- Get difference between two arrays: link
-
Slice an array:
arr.slice(start_index, end_index); ex: arr.slice(0,3); The last index is non-inclusive - Find certain values of an array: link
-
Sleep function:
function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } sleep(1000).then(() => console.log('sleep'))
- Set value after specific time interval: `setTimeout(() => {button.textContent = 'temp value'}, 1000);
-
Adding clear button for input field: link; link2
- Example in this repo: (link)[https://github.com/robbiehume/CS-Notes/blob/main/javascript/clearable_input.vue)
- Keep footer where it belongs: link
-
Get certain key value from list of JSON objects:
cats.map((c) => c.name)// get a list of all the names of cats -
Access key as variable:
obj[var_name] - Get browser to navigate to a URL: link
- Set element to same with as another element: link
-
Check if a variable is equal to one of 2 or more possible values:
['val1', 'val2', 'val3'].inlcudes(var_name)