JS Fiddle

JS Fiddle is a useful site to test out HTML, CSS, and Javascript snippets.


Backtick & String

const embeddedExpression = `Current Year: ${new Date().getFullYear()}`;
  • Use backticks ` in order to create a template literal string - string in Javascript that spans multiple lines and uses embedded expression ( i.e. ${variableName} )

Comments

// This line is a comment

/*
This block is also a comment
*/

Console Log

console.log>("message");
try{
   // do something
} catch (e) {
   console.error>("Do something failed: ", e.message)
} finally {
   console.log("Do something finished!")
}

Variables

  • const: declare constant; can&pos;t be reassign
  • let: declare declare block-scoped variables
  • var: avoid in modern code; delcare function-scoped variables (accessible even behondif and for loops statements)
  • ${variableName}: allow for clean injecting of Javascript variables/ expressions into the middle of string without having to variableName + 'string literal'
    • Only usuable in a String

Variable Types

  • string: &pos;hello&pos;
  • number: 5
  • boolean: true
  • object: { key: value }, [1, 2, 3], /regex/, new Date()
  • function: function () , class MyClass
  • bigint: 9007199254740992
    • MAX_SAFE_NUMBER = 9007199254740991
  • symbol: Symbol(x)
  • undefined: a variable that has not been assigned a value

Unconventional Cases

// Numbers
typeof NaN === "number"; // Despite being "Not-A-Number"
typeof Number("shoe") === "number"; // Number tries to parse things into numbers

// Strings
typeof typeof 1 === "string"; // typeof always returns a string
typeof String(1) === "string"; // String converts anything into a string, safer than toString

// Booleans
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()

// Objects
typeof null === "object"; // null is also treated as false and 0;

// Avoid
typeof new Boolean(true) === "object";
typeof new Number(1) === "object";
typeof new String("abc") === "object";

Notes

  • typeof can be used to identify type
  • instanceof can be used to check if object is sublcass of another
    • instanceof Array : check if something is an array

Data Structures

FeatureArrayObjectSetMap
Type of keys:Numeric indexes (0,1,2...)Strings or SymbolsValues only (no keys)Any value (objects, functions, primitives)
Allows duplicate values:YesN/A (keys must be unique)No (all values are unique)Keys unique, values can duplicate
Order guaranteed:Yes (insertion order)No (property order is mostly insertion-based but not reliable for all cases)Yes (insertion order)Yes (insertion order)
Iteration support:for, for...of, forEach, map, filter, reducefor...in, Object.keys/values/entriesfor...of, forEachfor...of, forEach
Primary use case:Ordered list of valuesKey-value pairs, fast property lookupUnique values collectionKey-value pairs with any key type
Performance for lookups:O(1) by indexO(1) by key (hash map under the hood)O(1) for presence checksO(1) for presence checks
Common methods:push, pop, map, filter, reduceObject.keys, Object.values, hasOwnPropertyadd, delete, has, clearset, get, has, delete, clear

Notes

  • Array is best for ordered lists and sequential data.
  • Object is the traditional key-value structure, but keys are limited to strings/symbols.
  • Set ensures uniqueness, useful for deduplication.
  • Map is more flexible than objects since keys can be any type.

Operators

  • %: modulo (remainder)
  • **: exponentiation
  • ++: increment
  • --: decrement
  • ==: equality (type conversion allowed)
  • ===: strict equality (no type conversion)
  • !=: inequality
  • !==: strict inequality
  • &&: logical AND
  • ||: logical OR
  • ??: nullish coalescing (returns right side only if left is null/undefined)

Conditionals

const value = 5;

// if / else
if (value > 3) {
  console.log("Greater than 3");
} else {
  console.log("Less than or equal to 3");
}

// ternary operator
const result = value > 3 ? "Big" : "Small";
console.log(result);

// switch statement
switch (value) {
  case 1:
    console.log("One");
    break;
  case 5:
    console.log("Five");
    break;
  default:
    console.log("Unknown");
}

Truthy & Falsy

JavaScript Truthy vs Falsy Values
Value / ExampleTypeBoolean ResultNotes
0, -0NumberFalsyZero values are falsy.
0nBigIntFalsyBigInt zero is falsy.
NaNNumberFalsyResult of invalid numeric ops.
""StringFalsyEmpty string only.
nullNullFalsyIntentional “no value”.
undefinedUndefinedFalsyUninitialized / missing value.
Non-zero numbers (e.g., 1, -1, Infinity)NumberTruthyAll non-zero numbers are truthy.
Non-empty strings (e.g., "0", "false", " ")StringTruthyEven "0" and "false" are truthy.
{}ObjectTruthyEmpty object is truthy.
[]ArrayTruthyEmpty array is truthy.
function() FunctionTruthyAll functions are truthy.
Symbol()SymbolTruthyAll symbols are truthy.
Non-zero BigInt (e.g., 1n, -5n)BigIntTruthyAny BigInt except 0n.
Objects like new Date(), new Map(), new Set()ObjectTruthyEmpty Map/Set are still truthy.

Loops

// for loop
for (let i = 0; i < 5; i++) {
  console.log(i);
}

// while loop
let count = 0;
while (count < 3) {
  console.log(count);
  count++;
}

// do...while loop
let j = 0;
do {
  console.log(j);
  j++;
} while (j < 3);

// for...of loop (arrays)
const arr = [1, 2, 3];
for (const num of arr) {
  console.log(num);
}

// for...in loop (objects)
const obj = { a: 1, b: 2 };
for (const key in obj) {
  console.log(key, obj[key]);
}

Classes

Same as Java classes

class Animal {
  constructor(name) {
    this.name = name;
  }
  
  speak() {
    console.log(`${this.name} makes a sound`);
  }
}

class Dog extends Animal {
  speak() {
    console.log(`${this.name} barks`);
  }
}

const dog = new Dog("Rex");
dog.speak(); // Rex barks

Importing

An alias can be used to replace really long names by using as (i.e.import {reallyLongName as newName} from 'script.js' )

  • Named Import: import any exported Javascript bindings (i.e. functions, variables/constants, classes, objects)
  • //In script2.js
    import {myFunction, MyClass} from "script1.js";>
    
    myFunction();
    const v = new MyClass();
    ------------------------------------------------
    //In script1.js
    export function myFunction{}
    export class MyClass{}
  • Default Import: import any exported default Javascript bindings
    • Only one default can exist in each Javascript file
    • Any new alias can be given to the default
    //In script2.js
    import NewNameClass from "script1.js";>
    
    const v = new NewNameClass();
    ------------------------------------------------
    //In script1.js
    export default class MyClass{}
  • Namespace Import: import ALL exported Javascript bindings
  • //In script2.js
    import * as myModule from "script1.js";>
    
    myModule.myFunction();
    const v = new myModule.NewNameClass();
    ------------------------------------------------
    //In script1.js
    export function myFunction{}
    export class MyClass{}
  • Side Effect Import: doesn't import anything, but instead run all the global code of the module
  • //In script2.js
    import "script1.js";>
    
    //myFunction is being run from the script2.js
    ------------------------------------------------
    //In script1.js
    export function myFunction{}
    myFunction();

Importing into HTML

The section in HTML Basics covered it.


Destructuring Syntax

Destructuring syntax can be used on arrays and objects to simplify code by using shorthand to unpack values into distinct variables

  • The values of an array can be unpacked into an array of distinct variables; likewise, the properties of an object can be unpacked into an object with the same number of dinstinct variables
  • When destructuring an array, the order of the distinct variables matter
  • When destructuring an object, only the name of the properties matter and not the order, but requires parenthesis around the whole statement
  • It's useful for destructuring function arguments
// JS

// Destructuring array (order matter)
const array = [1, 2, 3, 4, 5];
let first, second, rest;
[first, second, ...rest] = array;   
// first = 1, second = 2, rest = [3, 4, 5]

------------------------------------------------
// Destructuring object (order does not matter, but need () )
const obj = { 
    prop1: 1, 
    prop2: 2, 
    prop3: 3,
    prop4: { 
        a:10, 
        b:11, 
        c:12 
    } 
};
let prop1, prop2, prop3, prop4, rest, a;

( {prop1, prop2, ...rest} = obj );   // parenthesis around the whole statement to tell JS it's an object literal
// prop1 = 1, prop2 = 2, rest = { prop3: 3,  prop4: { a:10, b:11, c:12} }

( {prop3, prop4:{a} } = obj );  
// prop3 = 3, prop4 = undefined, a = 10 

------------------------------------------------
// Destructuring works well with function arguments
function myFunction(props) {
    const prop1 = props.prop1;
    ...
}
function myFunction( {prop1} ){
    ...
}

Computed Property Name

Normally, the keys of an object are literal, i.e. const obj = &123;key: 1 &125;; would have key itself as the key, but Computed Property Name allow Javascript to dynamically assign keys to object by replacing them with Javascript expressions.

  • In order to use the value of a variable or expression as an object's key, add brackets [] around it
    • const obj = &123;[expression]: value &125;;
  • This syntax can only be used on objects' keys
  • Useful in creating React forms or merging objects dynamically or when keys are not known until runtime
// JS

// Use for merging objects dynamically (i.e. updating an object field)
const field = "email";
const value = "alice@example.com";

const user = {name: "Alice", email: "old@example.com" };

// Create a new object with updated field dynamically
const updatedUser = {
  ...user,
  [field]: value 
};

          console.log(updatedUser);
// {name: 'Alice', email: 'alice@example.com' }

// JS

// Use for building API payloads based on user input 
const params = ["sortBy", "filter"];
const values = ["date", "active"];

const payload = {
  [params[0]]: values[0],
  [params[1]]: values[1],
};

            console.log(payload);
// {sortBy: 'date', filter: 'active' }

// JS

// Computed keys with expressions
const i = 1;
const obj = {
  ["item_" + i]: "apple",
  ["item_" + (i + 1)]: "banana"
};

console.log(obj);
// {item_1: 'apple', item_2: 'banana' }

Function Factory

funFactory(row) => () => {return(row)}

  • funFactory will return a function () => {return(row)} that always return the value of row
  • If row was an object, the reference will be saved by the factory; a copy or clone is needed to save the object's values
  • Can be useful for creating handlers to functions, configurating their parameters, and memoization
// Creating event handlers or callbacks
function clickLoggerFactory(message) {
  return () => console.log(`Clicked: ${message}`);
}

button1.onclick = clickLoggerFactory("button1");
button2.onclick = clickLoggerFactory("button2");

------------------------------------------------------------
// Configurating function parameters
function apiCallerFactory(apiUrl) {
  return async (endpoint) => {
    const res = await fetch(`${apiUrl}/${endpoint}`);
    return res.json();
  };
}
const githubAPI = apiCallerFactory("https://api.github.com");
githubAPI("users/octocat").then(console.log);

------------------------------------------------------------
// Memoization
function memoAddFactory() {
  const cache = {};
  return (a, b) => {
    const key = `${a},${b}`;
    if (!(key in cache)) cache[key] = a + b;
    return cache[key];
  };
}
const addMemo = memoAddFactory();
console.log(addMemo(2, 3)); // 5
console.log(addMemo(2, 3)); // cached result: 5