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:
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 overloadingconst 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 withmultiplelines`)
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 arrayfruits.unshift("lemons") // Adds value to beginning of arrayfruits.pop() // Remove last itemfruits.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.
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__.
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 reassignmentlet 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-declaredconst 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
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 indexfor(let i = 0; i < fruits.length; i++) { console.log(fruits[i])}// For loop with increments inside loopfor(let i = 0; i < 35; i++){ console.log(i); i += 10;}// For loop without incrementfor(let i = 0; i < 35;){ console.log(`${i} is the current value`); i += 10;}// Breaking out of a for loopfor(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 arrayconsole.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 indexingconsole.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 workfor(let key of person) { console.log(key)}
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 arraytodos.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 conditionconsole.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 > 10then(?) assign "red", else(:) assign "blue"
// Ternary operatorconst t = 10;// if condition THEN (?) value, ELSE (:) other valueconst 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.
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 necessaryconst 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:
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// -> 2console.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.