JavaScript documentation can be found at the MDN website

Tidbits

We can use "" or '' when writing strings:

const fname = "John";
const lname = 'Smith';
// Both are valid

The semi-colon at the end of a line in js files is not necessary but it is common because JS uses automatic semicolon insertion

To be more precise:

Firstly, a semicolon is optional only where there is a line break, a closing brace, or the end of the program. Semicolons are not optional between statements appearing on the same line. Additionally, a semicolon is not implied at the end of a line if the first token of the subsequent line can be parsed as part of the same statement.

Functional programming

Methods can be chained to produce powerful functional programming programs:

const items = todos.filter(function(todo) {
	return todo.isCompleted === true;
}).map(function(todo) {
	return todo.text;
})

JavaScript Data Types

In JavaScript, every value is either an object or a primitive value. A value is only a primitive when it is not an object.

Note

JavaScript is dynamically typed, so we do not need to manually define the data type stored in a variable; this is done automatically.

Note

In JavaScript, the value has a type, not the variable

Primitive Data Types

  • String defined with single or double quotes or backticks (for use as f-string)
  • Numbers as in Python (note that all numbers are floating point numbers)
  • Boolean true/false
  • null null
  • undefined undefined
  • Symbol (not very common)
  • BigInt Larger integers that number cannot hold (like smallInt, int, and bigint in SQL)

We can test types with `console.log(typeof ).

const name = "John"
 
console.log(typeof name);-> string
 
console.log(typeof 30.5); -> number
 
console.log(typeof null); -> "object"
 
console.log(typeof undefined);-> undefined

Info

typeof null returns “object” due to an early implementation of JavaScript, wherein objects had tags and values, and null was given a value of 0

Strings

String concatenation can be used by operator overloading or with template literals. The latter is like Python f-strings, except that it requires the use of back-ticks (`) when writing the string.

// Operator overloading
const name = "John";
const age = 30;
 
console.log("My name is " + name + " and I am " + age);
// Template String (like Python f-string)
console.log(`My name is ${name} and I am ${age}`)

We can also use template literals to create multi-line strings. Without the template literals we can to use the /n character plus a termination \ for each line:

console.log("String with \n\
multiple \n\
lines")

With a template literal we can do:

console.log(`String with
multiple
lines`)

Note

The use of backticks for template literals is mandatory. However, it is also possible to default to always using backticks for all strings.

Arrays

Arrays are much like Python lists. We initialize an array either via a constructor, or by using Python list-like syntax:

const numbers = new Array(1,2,3,4,5);
console.log(numbers);
 
  
const fruits = ["apples", "bananas", "oranges"]
console.log(fruits[1]); // Prints "bananas"
fruits[3] = "grapes";
fruits.push("mangoes") // Adds value to end of array
fruits.unshift("lemons") // Adds value to beginning of array
fruits.pop() // Remove last item
fruits.indexOf("bananas")

Note

In the example above I used const to declare the fruits array. I then modified the array, which const allows us to do. We just can’t re-declare it.

Link to original
JS Objects

Classes

A class in JavaScript is the exact same thing as Objects under the hood, but works as syntactic sugar to make it easier to use OOP paradigms. The main difference is the use of a class keyword and the call to a constructor function. In a way, this is no different than Python with class and def __init__.

// Classes
 
class Human {
    constructor(firstName, lastName, dob) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.dob = new Date(dob);
    }
 
    getFullName() {
		return `${this.firstName} ${this.lastName}`
	}
 
    getBirthYear() {
        return this.dob.getFullYear()
    }
}
 
const human1 = new Human("James", "Smith", "4-2-1990");
console.log(human1.getFullName())
console.log(human1.getBirthYear())

Note

When using classes to define methods, note that we do not need to assign a function to this method-name

Variables

  • var globally scoped, not used much anymore
  • let allows value reassignment
  • const constants which cannot be re-assigned
// Note that variables only need let/const the _first_ time they are declared, not during reassignment
let age = 30; 
age = 31; // This is allowed, overrides the value of age
 
 
// Also note that because age has been declared with let, it cannot be re-declared
const age =30;
age = 31; // This will fail
 
let name = "Adnan"
let/const name = "anything" // this will fail
 

Because we cannot re-declare a variable, it is common to simply initialize variables, like so (only with let):

let score;
let currentTime;
// Both of these return "undefined"
 
const score; // This will lead to an initialization error
Link to original

Loops

For

A for loop consists of four parts: the constructor, the initial assignment of the iterator, the condition and the increment:

for(let i = 0; i < 10; i++){
	console.log(i);
}
 
// For loop over an array index
for(let i = 0; i < fruits.length; i++) {
    console.log(fruits[i])
}
 
// For loop with increments inside loop
for(let i = 0; i < 35; i++){
	console.log(i);
    i += 10;
}
 
// For loop without increment
for(let i = 0; i < 35;){
	console.log(`${i} is the current value`);
    i += 10;
}
 
// Breaking out of a for loop
for(let i = 0; i <= 10; i+= 2) {
    if (i == 6) {
        break;
    }
    console.log(i)
}

Note

The condition in the loops above is a pass condition: the loop continues until the condition is false.

Caution

The for loop can be initialized with an undefined i variable. This will still run, but the condition will never be met, leading to an infinite loop.

For-of / for-in

In addition to the standard for loop, JS has for-in and for-of loops:

// For-in loop to iterate over enumerable keys of object
// It will return the _key_ of a dict...
console.log("For..in loop over object")
for(let key in person) {
    console.log(key);
}
 
// ...or the index of the array
console.log("For..in loop over array")
for(let key in fruits) {
    console.log(key)
}
 
// The two above means we need to reference the original object to find the value by indexing into it:
console.log("For..in loop over array with indexing")
for(let key in fruits) {
    console.log(fruits[key])
}
 
// For-of loop to iterate over _items_ in a collection
// Used to avoid the example above and get values without indexing
console.log("For..of loop over iterable")
for(let fruit of fruits) {
    console.log(fruit)
}
 
// An _object_ is not iterable, and thus for-of loops won't work
for(let key of person) {
    console.log(key)
}

Video example

While

console.log("While loop")
let i = 0;
while(i < 10) {
    console.log(i)
    i++;
}

High Order Array Methods

Rather than loping over an array, we can use various default built-in methods instead:

  • forEach calls function for each element
  • map calls function for each element, returns an array
  • filter

These methods all take a function as a parameter. The simplest example is to create a function that takes a variable.

const todos = [
    {
        id:1,
        text: "take out trash",
        isCompleted: true
    },
    {
        id:2,
        text: "meeting",
        isCompleted: true
    },
    {
        id:3,
        text: "sleep",
        isCompleted: false
    },
];
 
// foreach calls the callback function _once_ per item in the array
// thus it can be used to iterate over the array
todos.forEach(function(todo) {
    console.log(todo)
    console.log(todo.text)
});
 
// map calls the callback function _once_ per item in the array
// thus it can be used to iterate over the array
// note that map _returns an array_
console.log("map method")
const todoText = todos.map(function(todo) {
    console.log(todo)
    console.log(todo.text)
    return todo.isCompleted === true
});
console.log(todoText) // Returns array of booleans that state whether condition is met or not -> [true, true, false]
 
// filter calls the callback function _once_ per item in the array
// thus it can be used to iterate over the array
// note that filter _returns an array_ that contains the filter condition
console.log("filter method")
const todoCompleted = todos.filter(function(todo) {
    console.log(todo)
    console.log(todo.isCompleted)
    return todo.isCompleted === true
});
console.log(todoCompleted) // Returns subset of original array where the specific filter field matches

JS Conditionals

A simple conditional can be constructed with the if method:

if(condition) {
	// code
}

When doing equality comparisons, JS supports both double(==) and triple (===) conditionals. The double equality symbol does not match data types, only values.

 
const x = 10;
const y = "10";
 
if(x == 10) {
    console.log("x is 10")
}
 
if(x == y) {
    console.log("matches value")
}
 
if(x === 10) {
    console.log("matches value AND type")
}
 
if(x === y) {
    console.log("this will not print")
} else if(x === "10") {
	console.log("will also not print")
} else {
	console.log("data type and value don't match")
}

We can also do multiple conditionals with logical or/and:

const x = 4;
const y = 10;
 
if(x > 5 || y > 10){ // Logical OR
	// rest of code
}
 
if(x > 5 && y > 10){ // Logical AND
	// rest of code
}

Info

Single pipe | or ampersand & is the bitwise operator, rather than logical

Ternary operator (if true then this else that)

There is also a ternary operator, then, which is represented by a question mark ? and else, represented by a colon : . In this example, if x > 10 then(?) assign "red", else(:) assign "blue"

// Ternary operator
 
const t = 10;
// if condition THEN (?) value, ELSE (:) other value
const color = x > 10 ? "red" : "blue";
console.log(color)

Switches

The basic syntax for switches is as follows:

switch(color) {
    case "red":
        console.log(`color is ${color}`);
        break;
    case "blue":
        console.log(`color is ${color}`);
        break;
    default:
        console.log(`color is unknown.`);
}

The break is required because otherwise the default case will run even if a specific case is matched.

Link to original

Functions

Functions are declared with the function keyword and a function name with optional parameters:

function <funcName>(<params> (= <devault_value>)) {
	// code
}
 
addNums();

Arrow functions

Arrow functions were introduced in 2015, and they allow a similar but different construction for functions. The following two produce the same result:

function addNums(num1 = 1, num2 = 1) {
    return num1 + num2
}
 
const addNums = (num1 = 1, num2 = 1) => {
	return num1 + num2
}

The advantage of arrow functions is that they allow for easy one-liners when complex code doesn’t need to be wrapped by the function itself:

// Simplest arrow function:
const xPlusOne = x => x + 1;
 
// Note that "return" isn't necessary
const addArrowNums = (num1 = 1, num2 = 1) => num1 + num2
 
// This can also be used as a parameter for a method, i.e.
 
todos.forEach((todo) => console.log(todo))

Symbols

A Symbol is a primitive data type in JavaScript that is guaranteed to be unique. This means that we can create new Symbols as follows, and despite them being apparently equal, they are distinct:

const mySymbol = Symbol();
const namedSymbol = Symbol("myName");
 
console.log(Symbol() == mySymbol); // -> false
console.log(namedSymbol == Symbol("myName")) // -> false

Each of these Symbols has their own ID, and they are fully unique.

new <ClassName>, Symbols are declared without the keyword.

Unlike classes, where new instances of a class are declared with

We can use symbols to hide properties in a class. For example:

const character = {
	name: "Jane Smith",
	class: "Wizard",
	species: "Argonian",
	[Symbol("admin")]: false
}
 
for (const key of Object.keys(character)) {
	console.log(key)
} // -> name, class quote

Note how in the iterator the Symbol will not appear.

Note

The bracket syntax in the Character class allows us to evaluate the statement. Thus, adding a property of [4+5]: "age" would be presented as 9

If we want to see the Symbols associated with an object we can use Object.getOwnPropertySymbols(<object name>)

Hoisting

To understand hoisting, remember two basic principles: 1) variables (let/const) work as normal, always. 2) functions are read and defined first, regardless of where in the code they are.

An exception to this is the var keyword. When present, JS begins by initializing var variables as undefined. In other words, we can use var at the bottom of a script, but they will be hoisted to the top and set to undefined until the line of code that defines that var:

var a = undefined;
console.log(a)
var a = 2;
console.log(a)
// -> Undefined
// -> 2
 
 
console.log(someFunc(1, 2))
function someFunc(a, b) {
	return a + b
}
// Works because function is hoisted
 

Adding JS to a page

JavaScript can be loaded into an HTML document with the script tag. Note that the location of the tag matters: if its towards the beginning, the browser will start loading HTML, then be bloked by loading the JS file, then continue.

An old alternative was to place the tag at the end of the document, but this becomes a problem because the browser will not start to download scripts until the tag is reached. As a result, on large websites the scripts might not start to download until the very end, which may delay interactivity.

A modern solution is to either use async or delay. The former means that the script will download separately and async, but it does imply that the JS file can potentially execute before other things are finished downloading (note that this can be handled by using DOMContentLoaded notifications such the bulk of the JS file isn’t run until the end.)

defer means that scripts will be downloaded in order, and will also not block browser loading. However, defer scripts will only run once everything has been loaded.

StackOverFlow discussion