A few compilation of notes that will be updated with many more + different UI
Nice little summary I found later. Use to refresh topics, not to learn them:
100+ JavaScript Concepts you Need to Know - YouTube
.js
fileTo add a source attribute into the html file use <script>
tag
Recommended adding it at the bottom of the scrypt tag so it's loaded when all your elements are created
<body>
<p>Paragraph</p>
<p>Test</p>
<script src="file.js"> </script>
<body>
An instruction to the computer or browser to do something and when your statement is done, you put a semi-colon to end theline
let x = 5;
Building blocks of javascript
Three types to declare a variable.
var
used if you must support old browsers (used from 1995 to 2015) and are function scoped ()
const
used when the value or type is not supposed to change.
let
Used when you can't use const
and when a variable is not defined. Let is block scoped {}
var text = "something";
let age = 50;
let dog;
const on = true;
Variables should not start with a capital unless they are a class, the usual writing style is CamelCase
Not a rule but might be useful, use const
as the default unless you need to change the variable.
There are 7 types of data
String - anytime you have some text.
Number - Numbers that do and don't have decimals.
Object - Everything in JavaScript is an object.
Null - Nothing
Undefined - Nothing
Symbol - Gives a unique identifier
Serie of characters like "John Doe". Can use single or double quotes
You can set a backlash `` to escape taking something like JavaScript syntaxt, instead taking it as text
const name = "John Doe";
const name2 = 'John Doe';
// By using a single quote JavaScript will think you are finishing your
// string, to prevent this use the backlash
const name3 = 'She's having a party'; // Error
const name4 = 'She's having a party';
To use a string in multiple lines you can use Backticks `
const text = `printing this
will contain the "return" character each enter
which means new line
`;
const html = `
<div>
<h1> Hi </h1>
</div>
`
// we can use this to insert a string into HTML
Concatenation when two or more strings are combined into one
Interpolation when you include a variable between strings
const name = "John Doe";
const greeting = "Hi, I am " + name + ". Nice to meet you!";
It's also possible to interpolate by setting the string using Backticks ` and to represent the variable or statement use ${}
const name = "John";
const hello = `My name is ${name}, hi`;
//or
const name = "John";
console.log(`Hi my name is ${name} nice to meet you`);
typeof is a keyword that will show you what kind of type is a variable
const something = 100;
typeof something; //will return "number" type
There's multiplication, division, addition and substraction in numbers
const a = 5;
const b = 10;
a + b; //returns 15
a * b; // 50
a - b; // 5
a / b; // 0.5
Helper methods
If you type "Math." you can find some tools to change numbers, for example Math.round(20.7)
will round it to 21, Math.floor()
will round to the bottom, Math.random()
will give you a random number between 0-1
Example
const cakes = 2;
const kids = 10;
const cakeForEach = Math.floor(cakes / kids);
console.log(`Each kid gets ${cakeForEach} of a cake`)
NaN means Not a Number. Happens when try to do math with something that is not a number, for example 10 / "dog"
typeof NaN; //returns "number"
Small explanation, everything in JavaScript is an object
Used of collections of data and collections of functionality
Used to group things together, you can initialize an object by using curly brackets and a semi-colon at the end, inside you can store properties
and values
separated by a comma
const person = {
first: "John",
last: "Doe",
age: 40
};
To access the properties you can use a dot
person.first //returns "John"
person.last //returns "Doe"
person.age //returns 40
There's two way to express nothing in JavaScript
If you create a variable called test that has nothing in it, and try to print it console.log(test);
you will get an undefined
because the variable exist but does contains nothing.
let test;
console.log(test);
// returns "Undefined"
While undefined
is a variable that has not yet have a value set to it, null
is nothing, you set the value to have nothing in it
let somethingUndefined; //has an undefined value
const somethingNull = null;
Boolean are true or false, like a light switch, it's on or it's off
let isOn = false;
You can get a true or false by comparing values, one equal sign is to add (1+1), but to compare you should use 3 equals signs (1 === 1), and returns a boolean value because you are asking if 1 is equal to 1
let age = 20;
const canYouDrink = age > 21;
console.log(canYouDrink); //returns false, since age is less than 21
let age = 21;
const isTrue = age === 21; //returns true since age has a value of 21
Difference between two equals (1==1) and three equals (1===1)
Two equals check if the values match, but not the type
let age = 20;
console.log(age == "20");
//returns true, even while we are comparing a number to a string
But three equal also check for the type
let age = 20;
console.log(age === "20");
//returns false, because age contains a number, 20, and we are asking
// if it is equal to a String that contains 20
Allows us to set a group together a set of statements
Can take in data known as arguments
Regular functions declared are "hoisted" at the top of the file, that means if you try to call the variable "before" defining it, it will work since it's hoisted up
There are some functions already built in, such as
parseFloat("1.123") //returns 1.123 as a float number
toString() //returns true or false
Date.now() //Throws how many miliseconds since 1970
Functions are created and defined and you can run it
To define a function you place parenthesis and you open a function block with curly braces
function calculate() {
console.log("Running");
}
//function call/run
calculate();
function calculateTax() {
const total = 100 * 1.13;
console.log(total);
}
When you declare a variable inside a function, there's something called scope tha does not allow us to use that variable outside of the function
function test() {
const hello = "This is a variable inside a function";
}
//This will create an error, since hello only exists inside the function
// block
console.log(hello);
To be able to use it outside the function, we would need to return the value and assign it to a variable
function test() {
const hello = "This is a variable inside a function"; //temporary variable
return hello;
}
const testFunction = test();
console.log(testFunction); //prints "This is a variable inside a function"
You can also work with the function, since it's return value is the one we declared before
function payment() {
const hello = 10; //temporary variable
return hello;
}
const myTotal = payment();
console.log(`Your total is ${payment()}, thank you`)
// prints "Your total is 10, thank you"
When you use variables inside the function that are declared outside the function, you need to pass data
Inisde the parenthesis you place the expected data as placeholder
function calculateBill (billAmount, taxRate) {
const total = billAmount * 1 + taxRate;
return total;
}
const myTotal = calculateBill(100, 0.13);
First you define some placeholder, so when you call your function, you give the actual values as arguments that you want it to use, this way the function is reusable and you can give the values you want when it's called.
You can also pass a variable as an argument when calling the function, but that variable will be recognized inside the function as only the value, not the variable's name.
function calculateBill (billAmount, taxRate) {
const total = billAmount * 1 + taxRate;
console.log(total);
return total;
}
const meal = 100;
const tax = 0.3;
const myTotal = calculateBill(meal, tax);
In this example we are passing meal which has a value of 100, JavaScript will recognize inside the calculateBill
function, that you pased a value of 100 and will asign it into the first placeholder you had, this means billAmount
will have a value of 100, but we will not be able to use the meal
variable inside, only it's value.
You can set a default value for the parameters in case there is no value passed in. This will print in the console "Hi John doe, how are you?", but if we instead pass a string as an argument like "Fer", the console will say "Hi Fer, how are you?"
function test(name = "John Doe") {
console.log(`Hi ${name}, how are you?`);
return `Hi {name}, how are you?`;
}
test();
It is possible to pass a function to another function's paremeters
function ageCalc(age) {
return age + 10;
}
//the name of the placeholder is not important for this
//it will never collide with the one above because of the scope
function ageGreet(age) {
return `Your age in 10 years will be ${age}, congrats`;
}
ageGreet(ageCalc(10));
This will first call the function ageGreet
, but in order to get the value the program will run ageCalc
with a value of 10. Returning a 20, once it has that value it will run ageGreet
and will print "You are in 10 years will be 20, congrats"
Functions without a name, must be used inside function expressions
, inside a const
function (age) {
return age + 10;
}
//gives an error at first
A function stored in a variable (goes with a semicolon since it's a variable declaration).
This function is not hoisted up since it's a variable
const ageGreet = function (age) {
return age + 10;
};
A function with concise syntax
Are anonymous functions
Uses a "fat arrow" =>
to represent that it is a function
//Normal function
function inchToCM (inches) {
return inches * 2.54;
}
// Converted into anonymous, changed function and placed fat arrow
// at the right of the parenthesis
const inchToCM = (inches) => {
return inches * 2.54;
};
//Remove the function block and remove return
const inchToCM = (inches) => inches * 2.54;
//another example with the same ageCalc function we had above
const ageCalc = (age) => age + 10;
Immediately Invoked Function Expression
It's possible to invoque a function immediatly if we make it anonymous and use parenthesis around it
(function () {
console.log("Hi");
})();
A function inside an object
For example, console.log()
Console is an object, log is a function
You can also skip writing "function()" by just adding parenthesis to the object propertie
const name = {
name: "John",
sayHi: function(){
console.log("Hello friend");
}
}
//Short hand method
const yell = {
name: "John",
yellHi() {
console.log("HELLO FRIEND");
}
}
In order to use this we would need to first call the object, by typing for example in our console:
name.sayHi();
yell.yellHi();
A function that'll happen when something is done, for example when someone clicks something runs a function.
Here's an example by creating a button in our HTML file, we give it a class called "click", save the click as a variable and add an event listener so it runs a function everytime it's clicked
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<button class="click">click me</button>
<script src="types.js"></script>
</body>
</html>
const name = {
names: "John",
sayHi: function(){
console.log("Hello friend");
},
//Short hand method
yellHi() {
console.log("HELLO FRIEND");
}
}
const button = document.querySelector(".click");
button.addEventListener("click", name.yellHi);
Gives a delay
setTimeout(function() {
console.log("done");
}, 1000)
Ways to fix a problem, mostly in Dev Tools (Inspect element on web)
console.log() - Prints something in the console
console.error() - The same but shows like an error in the console
console.warn() - Same but with warning
console.table() - When you have a list of objects shows a table with that info
console.count() - Counts how many times a function or variable has been called
console.group() - Creates a collapsable menu in the console that groups everything inside a function, it needs to be closed with console.groupEnd()
and have the same string than the opening
function doALotOfStuff (abc) {
console.group("Doing stuff");
console.log("Hey");
let conversion = abc = 10;
console.error(conversion);
console.warn("Be careful");
console.groupEnd("Doing stuff");
}
When you get an error, it's important to read what it says, since you will be guide into where the error is and what is the trace into that error, for example if you are trying to run a function that calls a function that has an error, you'll get something like this:
"Uncaught ReferenceError: doesntExist is not defined
`at greet (debugging.js:47)`
`at go (debugging.js:52)`
`at bootstrap (debugging.js:58)`
`at debugging.js:61"`
Reading from top to botton, you can see that doesnExist is not defined, you can find it in the function greet inside the file called debugging.js in the line 47, the next line says that greet is called inside the function go, which is in the debugging.js file in the line 52. That is important in order to trace where an error is
You can to the Dev Tools in a web (inspect element) and see how those were created
If you go to the console and type $0
you'll see what you have selected in the elements tab, it's possible to get a value by typing $0.value
$("p") - will show the first paragraph
$$("p") - will show all of the paragraph elements
If you place a debugger statement the program will pause at were the debugger was placed to inspect each lines to find an error
function test(grade) {
console.log(`Your grade is: ${grade}, congrats`);
debugger;
console.log(`Hello`);
}
test("S+")
Open Dev Tools and go to network tab to see how it's timed when you use functions to request, or API's
In Dev Tools you can right click an attribute, if you select Break on, it'll show how a modification is done
Answers where are the variables available to use
Var
variables are attached to the window object and they are function scoped
Are available anywhere in the application, they are outside functions, or module, or statement, etc.
When variables are created inside of a function they can't be used outside
When you have curly brackets the variables inside are scoped in there
const age = 100;
function go() {
const hair = "blonde;"
}
console.log(age);
console.log(hair); // Will throw error since hair is a variable
// that's inside go function
if (1===1) {
let age = 1;
}
console.log(age); //also throws an error
if (2===2) {
var age = 2;
}
console.log(age) //this one will work since var is function scoped
If a variable is not found inside a Scope, it will go out one "block" and look for it, if it's not it'll go one up again
const age = 100;
function person () {
const hair = "blonde";
console.log(age);
console.log(hair);
}
age
is not inside the function scope, but it will look for it going up one more level
Regular functions declared are "hoisted" at the top of the file, that means if you try to call the variable "before" defining it, it will work since it's hoisted up
There's also variable hoisting, but it will not hoist the value
console.log(age);
var age = 10;
Will log "undefined" since it moved up "var age;" but not the assignment of the value
When a function is done, the scoped variables inside of it that are NOT returned are not available to use.
But using closures it's possible to use that variables from outside the function. JavaScript is able to create a function inside a function that can still reach the parent scope
function outer() {
const outerVar = "I'm an outer variable";
function inner() {
const innerVar = "I'm an inner variable";
console.log(innerVar);
console.log(outerVar); // Will print outerVar when running -
} // innerFunction, even when it's not in this scope and ran before
return inner;
}
const innerFunction = outer(); // We returned inner() so it's actually inner
innerFunction();
When creating a variable like innerFunction
, the function will run to give that variable something.
We can still access the outerVar variable by running the child function that includes a variable on the parent function
When you have functions inside of functions and you are able to access the closure inside of that.
What's happening here is that when you create the const sayHello
you are giving the first function the argument of "hello", then returns the inner function. When you call the const sayHello
and you give it an argument, the inner function is the one that will take that and store it into "name", then uses the myGreet
const that was declared before.
function createGreeting (greeting = "") {
const myGreet = greeting.toUpperCase();
return function(name) {
return `${myGreet} ${name}`;
}
}
const sayHello = createGreeting("hello");
const sayHey = createGreeting("hey");
console.log(sayHello("Sam"));
console.log(sayHey("Kath"));
Each time hockeyGame or soccerGame are used, the score will increment by 1
But each one will have a different counter, since we created two win functions by using the createGame
function two times, they each have their own private variable score. It's not accessible
function createGame (gameName) {
let score = 0;
return function win() {
score ++;
return `Your game ${gameName} score is ${score}`;
}
}
const hockeyGame = createGame("Hockey");
const soccerGame = createGame("Soccer");
When you write HTML and you view it on the browser, the browser turns into something called the Document Object Model (DOM).
Is an object and is everything about the currently open window, there are some useful commands like window.location, innerWidth
The entire document is accessible to us with the document
keyword
It's a higher level object than the window since gives more information not only for the browser but the devide itself, things like webcam, audio access, battery level, gps coordenates
Before you can go with the items you need to first select them and access to a specific element on the page and once we have it we can now add content to it, add listeners to it
There's two main ways to work with selecting elements usually used after document.
Will always give the **first** matching element
const p = document.querySelector("p");
console.log(p);
Gives a node list
const d = document.querySelectorAll("div");
console.log(d);
Both take one argument and it's almost the same selector from CSS
It's possible to get a parent child selectors
const img = document.querySelecto(".item img");
Will print a nodelist of the images inside an item class
const img = document.querySelector(".item img");
Will print the first image inside an item class
You can also do it like this
const item2 = document.querySelector(".item2");
const item2Image = item2.querySelector("img");
console.log(item2Image);
You can run querySelector off of that item so it only search in a limited scope.
document.querySelector will search in all the page and querySelector as a method of another element will only look inside that element
It's also possible to use document.getElementById()
to do the same but using an id instead of a class in the html file but seems to be old and replaced by querySelector
document.getElementById("p");
HTML elements are objects with a lot of properties and we can access to those properties and change them, this way it's possible to change a text of an element
(You can use console.dir()
to get the properties of an object)
Let's say we are gonna work with our h2 element
<div class="item">
<img src="https://picsum.photos/200">
<h2>Sub Div </h2>
<p>Hi I'm an item</p>
</div>
const header = document.querySelector("h2");
console.log(header.textContent); //prints "Sub Div"
header.textContent = "changed text";
console.log(header.textContent); //prints "changed text" since h2 content changed
textContent gives the text but also the style
innerText only gives the text
innterHTML gives only the elements inside another element
console.log(header.innerHTML);
Sub div
console.log(header.outerHTML);
<h2>
Sub div
</h2>
<p class="pizza">Our pizza has tomato, pepperoni</p>
const pizzaIngredients = document.querySelector(".pizza");
pizzaIngredients.innerText = `${pizzaIngredients.innerText} and cheese`;
This can be a bit slow in big projects since reloads the whole line
It's a method to add adjacent text to an element. Takes two arguments
element.insertAdjacentText(position, element);
beforebegin
before the element itself (above the <p>
element for example)
afterbegin
inside the element before it's first child
beforeeend
inside the element after the last child
afterend
after the element itself
<p class="pizza">Our pizza has tomato, pepperoni</p>
const pizzaIngredients = document.querySelector(".pizza");
pizzaIngredients.insertAdjacentText("beforeend", ` and cheese`);
It's possible to add and remove classes with:
element.classList.toggle(nameOfTheClass);
classList.add();
classList.remove();
classList.toggle();
classList.contains();
<body>
<img class="bigImg" src="https://picsum.photos/600"/>
</body>
const bigPic = document.querySelector(".bigImg");
console.log(bigPic.classList);
This will first print a DOMTokenList where we can see all the classes of the element
There's something called "Proto" that will show many methods that you can call against the element we have
const bigPic = document.querySelector(".bigImg");
bigPic.classList.add("amazing");
This will add the "amazing" class to the currently selected element, and we can work with that class in CSS, this way we can create an image that changes it's form to round whenever we want
.amazing {
border-radius: 50%;
}
We can create a small transition adding and removing classes (HTML, CSS, JavaScript)
<body>
<img class="bigImg" src="https://picsum.photos/600"/>
</body>
.bigImg {
transition: all 2s;
}
.round{
border-radius: 50%;
}
const bigPic = document.querySelector(".bigImg");
bigPic.classList.toggle("round");
Everytime we use this method in the console it will change to round or not round with an animation.
But we can add an event listener for it to do it each time you hover on it
const bigPic = document.querySelector(".bigImg");
function toggleRound () {
bigPic.classList.toggle("round");
}
bigPic.addEventListener("mouseover", toggleRound);
bigPic.addEventListener("mouseout", toggleRound);
This will have the image round by default, when your mouse moves over the image it will loose the round class and change it's form, then whe you move out it changes into round again
You can access all the attributes that are on something and give them values to those attributes with "setters", you can also get them with "getters".
Setters will give this attributes to the element we had selected
const bigPic = document.querySelector(".bigImg");
bigPic.alt = "Cute cat"; //setter
bigPic.width = 200; // setter
console.log(bigPic.alt); //getter
Gives you the attribute of an element
const bigPic = document.querySelector(".bigImg");
const something = bigPic.getAttribute("alt"); // asking to get the alt attribute
console.log(something); //logs the alt attribute of bigPic
Replace or add an attribute to an element, the difference between this method and using just declarations like above is that if you need to work with non-standard attributes you will have to use .setAttribute method (standard attributes are the usual alt, width, title, src, etc. Non standard can be the ones you create)
const bigPic = document.querySelector(".bigImg");
bigPic.setAttribute("alt", "Bigger with transition");
You can attach metadata attributes by writing "data-" before, e.g. "data-name", "data-description", in order to access this data you would need to create a variable and access with .dataset
<p class="toy" data-item="First Toy">Hi I'm an item</p>
const toy1 = document.querySelector(".toy");
console.log(toy1.dataset);
This will create a DOMStringMap with a list of the data with "item"
The getter can load before the image is downloaded, to solve this there's an eventListener that uses "load" for it to wait until all HTML, CSS and JavaScript are loaded to do whatever you want it to do
bigPic.addEventListener("load", function() {
console.log(pic.naturalWidth);
});
element.createElement()
To create an element in JavaScript or during a process or event, we use .createElement()
method and write what element we want, this will create the element but not the content or another elements on it, so we need to do it too
const myParagraph = document.createElement("p");
myParagraph.classList.add("Paragraph");
myParagraph.textContent = "Paragraph text";
const myImg = document.createElement("img");
myImg.src = "https://picsum.photos/600";
myImg.alt = "cute sky";
myImg.width = 400;
const myDiv = document.createElement("div");
myDiv.classList.add("Container");
In order to place this where we want, we need to use an API by using something called appendChild();
element.appendChild()
To place an element as a child of another element we use appendChild
The element will contain the variable that has the class of the element we want
The inserted child will go in the parameter, inside the parenthesis
const divBelow = document.createElement("div"); // Creating element
divBelow.classList.add("below"); // Giving class attribute to that div
const textBelow = document.createElement("p"); // creating element
textBelow.classList.add("paragraph"); //Giving class attribute to paragraph
textBelow.textContent = "I like cats"; // Giving text to paragraph
divBelow.appendChild(textBelow); // Inserting the paragraph inside div
const firstFigure = document.querySelector(".second-div");
// Creating variable that contains the selected area where we'll place
firstFigure.appendChild(divBelow);
// Placing the div into the HTML which has the paragraph inside
The best way to do this is by adding the elements that are going to go inside the div into the div, then AFTER that, adding the div into the html later
Because if you add the div first, it will reload each team to add every paragraph you have inside that div. Like this example:
document.body.appendChild(myDiv); //adds created div into the body
myDiv.appendChild(myParagraph); //adds paragraph into the div
myDiv.appendChild(myImage); //adds image into the div
What this will do is that first it will create an empty space in the html, then it will go again and add the paragraph to it, then it will go again and add the image to it. Causing a reflow 3 times
Instead doing it in this other order
myDiv.appendChild(myParagraph); //adds paragraph into the div
myDiv.appendChild(myImage); //adds image into the div
document.body.appendChild(myDiv); //adds created div into the body
Will first add those paragraphs and images into the div and then display the div with all together, so it's a more efficient way causing a reflow 1 time only
element.insertAdjacentElement(position, element);
Could be also interpreted as: "in relation of this first element... place the second element in this position"
beforebegin
before the element itself
afterbegin
inside the element before it's first child
beforeeend
inside the element after the last child
afterend
after the element itself
We can create a h2 element that contains something and we can place it wherever we want like in this example
const myDiv = document.createElement("div");
myDiv.classList.add("nice");
const myParagraph = document.createElement("p");
myParagraph.textContent = "hello this is a paragraph";
const heading = document.createElement("h2");
heading.textContent = "text sample";
myDiv.appendChild(myParagraph);
document.body.appendChild(myDiv);
myDiv.insertAdjacentElement("beforebegin", heading);
Doing this you are placing a <h2>
element that contains "text sample" before the <div class="nice">
, so you are inserting a whole element above the other element that you just created
const divBelow = document.createElement("div");
divBelow.classList.add("below");
const textBelow = document.createElement("p");
textBelow.classList.add("paragraph");
textBelow.textContent = "I like cats and pizza";
divBelow.appendChild(textBelow);
const firstFigure = document.querySelector(".second-div");
firstFigure.appendChild(divBelow);
const catImg = document.createElement("img");
catImg.alt = "Cute cat";
catImg.src = "https://t4.ftcdn.net/jpg/00/97/58/97/360_F_97589769_t45CqXyzjz0KXwoBZT9PRaWGHRk5hQqQ.jpg";
catImg.width = 100;
// pizza
const listContainer = document.createElement("ul");
listContainer.classList.add("list-container");
const item1 = document.createElement("li");
item1.textContent = "Cheese";
const item2 = document.createElement("li");
item2.textContent = "Tomato";
const item3 = document.createElement("li");
item3.textContent = "Pepperoni";
const item4 = document.createElement("li");
item4.textContent = "Not Onion";
const item5 = document.createElement("li");
item5.textContent = "Bread";
listContainer.appendChild(item1);
// listContainer.appendChild(item2);
listContainer.appendChild(item3);
// listContainer.appendChild(item4);
listContainer.appendChild(item5);
item3.insertAdjacentElement("beforebegin", item2);
item3.insertAdjacentElement("beforeend", item4);
listContainer.insertAdjacentElement("beforeend", catImg);
const clickButton = document.querySelector(".click");
// const showList = () => divBelow.appendChild(listContainer);
function showList () {
divBelow.appendChild(listContainer);
}
clickButton.addEventListener("click", showList);
.innerHTML
You can use .innerHTML
as a getter and as a setter (Needs to be used with backticks ` )
const something = document.querySelector(".first-div");
console.log(something.innerHTML); // Prints the content of the div
And to change the HTML elements inside you can use it as a setter, can be uses instead of the createElement() method (it does not need to be indented)
const something = document.querySelector(".first-div");
something.innerHTML = `
<div>
<h1> Title </h1>
<p> something </p>
</div>
`;
Adding HTML from strings does not count as an html element, this means you can't add attributes to this since it's recognized as a string, not as HTML elements
You can also add "${}" to this strings
const something = document.querySelector(".first-div");
const src = "https://picsum.photos/200"
const alt = "Random"
const newHTML = `
<div>
<h1> Title </h1>
<img src="${src}" alt="${alt}"/>
</div>
`;
something.innerHTML = newHTML;
Turns a string into a DOM element in order to add attributes to an added HTML from a string
const fragment = document.createRange().createContextualFragment(newHTML);
const change = newHTML.querySelector("img");
change.alt = "Cat";
change.remove();
Cross-site scripting
It's possible that by using the methods above, you let users be able to inject code and write elements with the strings they can write like usernames, they can write <script>
tags into it to do whatever they want like draining someone's bank account.
Traversing means going up, down, over, you often need to select an element based on their position like grabing the parent element of a button
Difference between node and element
Nodes are in the DOM aka Document Object model. In the DOM, all parts of the document, such as elements, attributes, text, etc. are organized in a hierarchical tree-like structure; consisting of parents and children. These individual parts of the document are known as nodes. https://i.stack.imgur.com/ocR0a.png
The topmost node is the root node (Document Node) of the DOM tree, which has one child. HTML attributes such as id, class, title, style, etc. are also considered as nodes in DOM hierarchy.
console.log(something.children);
console.log(something.firstElementChild);
console.log(something.lastElementChild);
console.log(something.previousElementSibling);
console.log(something.nextElementSibling);
console.log(something.parentElement);
.children - shows the childs of an element
.firstElementChild -
.lastElementChild -
.previousElementSibling - gives you the item that's before
.nextElementSibling - next item besides it
.parentElement - Gives you the parent, can be used multiple times to keep going up
Method .remove()
will remove an element, but that will be kept inside the memory, so it's still possible to print it even after it was removed:
const text = document.createElement("h1");
text.textContent = "I like pizza";
const test = document.querySelector(".test");
test.appendChild(text);
text.remove();
console.log(text.innerHTML);
element.addEventListener("event", callbackFunction);
When an element that's on the page is clicked, it's hovered, when they're dragged they emit events When this happens we can use an EventListener to listen this and react to them
Takes two arguments, three steps for events:
First get the element in order to listen it
const active = document.querySelector("event-button");
The listen the event
active.addEventListener("click", /*function*/ );
And then do something with it adding a function
active.addEventListener("click", function() {
console.log("clicked");
});
The second argument for the event listener is a function, that we can create previously
const button = document.querySelector(".event-button"); //get event element
// function handleActive() {
// console.log("clicked");
// }
const handleActive = () => console.log("clicked"); //callback function
button.addEventListener("click", handleActive); //listen and use the function
Not a rule but you can write handleSomething as the name of a function that will get passed in to an event handler
Binding means a function that's bound to an element
const button = document.querySelector(".event-button");
const handleActive = () => console.log("clicked");
button.addEventListener("click", handleActive);
button.removeEventListener("click", handleActive);
//will stop listening to the click event on this button
It's possible to have our own events, but here are the common events:
If you have multiple elements selected, you will not be able to addEventListener them
// Wrong way
const buyButtons = document.querySelectorAll("button.buy");
buyButtons.addEventListener("click", function() {
console.log("clicked");
}); //error since we used querySelectorAll
Is a method that will run a function for each item in the node list of the selected element
const allBuyButtons = document.querySelectorAll("button.buy");
const clicked = () => console.log("hello");
allBuyButtons.forEach(function(button) {
button.addEventListener("click", clicked);
})
Note that we use "placeholder" to reference what the navigator is giving us
We get an event listener run for each, so all buttons with the class buy will do the same
const buyButtons = document.querySelectorAll("button.buy");
buyButtons.forEach(something => {
something.addEventListener("click", () => {
console.log("somethingTest");
});
});
Used to get the event trigger, in this example a click in the window node We can save the event.target into a variable and use it to add elements or change anything
window.addEventListener("click", function(event){
console.log("Window clicked");
const eventTarget = event.target;
console.log(eventTarget);
eventTarget.setAttribute("data-item", "tomato");
eventTarget.classList.add("pizza");
});
Or we can access to attributes by using target in the parameter event
const all = document.querySelectorAll("button.buy");
function handleClick (event) {
console.log("Bought");
console.log("$" + parseInt(event.target.dataset.price));
// Access to the dataset "price" and prints it
// parseInt is used because it was still a string
}
all.forEach(function (button) {
button.addEventListener("click", handleClick);
})
When something is clicked, the click in reality goes through all the elements and nodes until it reaches the target event
Then it will bubble up triggering clicks on all the elements, so while you are clicking a button you are also clicking the window or other elements above, this could trigger more than one events, one the click of the button and other in the body or window.
To stop that it's possible to use event.stopPropagation();
, so when the event happens it won't go up again and trigger the possible events above the element
It's also possible to do the opposite, listen to something in the capture phase before going up using capture
, so the upper nodes elements would trigger their events before and not while bubbling up
target.addEventListener(type, listener[, options]);
This means the first argument is the type, in this example "click", the second argument is the listener which is the function that should be run and the third argument is options, that can include capture
, once
, passive
, etc
function handleClick(event) {
console.log("window clicked");
console.log(event.target);
}
window.addEventListener("click", handleClick, {capture: true});
Full example:
const all = document.querySelectorAll("button.buy");
function handleClickButton (awa) {
console.log("Bought");
console.log("$" + parseInt(awa.target.dataset.price));
}
all.forEach(function (button) {
button.addEventListener("click", handleClickButton);
})
function handleClickWindow(event) {
console.log("window clicked");
console.log(event.target);
}
window.addEventListener("click", handleClickWindow, {capture: true});
Because we are using capture, the window function event will be triggered first, after that, the button event will happen
There's elements that have a default use, for example when we click a link it will automatically send us to the website, using preventDefault()
will stop this, for example when using twitter and when you click anything instead of going there, it shows a message of login
const hyperlink = document.querySelector(".youtube-link")
hyperlink.addEventListener("click", function(event){
event.preventDefault();
console.log(event);
});
Or create a confirmation before going to a page
const hyperlink = document.querySelector(".youtube-link")
hyperlink.addEventListener("click", function(event) {
const confirmation = confirm(
"This website might be malicious, do you want to proceed?"
);
if(!confirmation) {
event.preventDefault();
}
}):
Accesibility is important, some people do not have a mouse so they won't be able to access to parts of your website that listen for "clicks"
Using tab to move around the page does not work in images, we would solve this by adding tabindex=0
<img class="cat" tabindex="0" src="https://picsum.photos/200">
const catPhoto = document.querySelector(".cat");
function handlePhotoClick(event) {
console.log("Photo clicked");
}
catPhoto.addeventListener("click", handlePhotoClick);
catPhoto.addeventListener("keyup", handlePhotoClick);
This way the event would happen when tabbing onto the image
It's possible to use an if to make it work better by needing them to press enter to go into the image
Note: Event keys are the value of the key pressed, to get more meta data from any key we need to be listened, use: https://keycode.info
<div role="button" tabindex="0">Something</div>
<a href="http://youtube.com">
<img class="cat" src="https://picsum.photos/200" alt="Cat">
</a>
const catPhoto = document.querySelector(".cat");
console.log(catPhoto)
function handlePhotoClick(event) {
if (event.type === "click" || event.key === "Enter") {
;
}
}
catPhoto.addEventListener("click", handlePhotoClick);
catPhoto.addEventListener("keyup", handlePhotoClick);
Statements expect booleans or conditions
If the condition in the parenthesis is true runs the code in the curly brackets right after, if not, it will keep going until it's true from top to bottom
if (10 < 2) {
console.log("nope")
} else if (11 > 10) {
console.log("yes")
} // console logs yes since it skips first one
Convertir una cadena de texto a un string removiendo o cambiando caracteres
This slugify would replace the s
character which is space, for _
a underscore
The first argument is the sentence, and the second if it's true or false, if the second argument is true run the first line which is replace the spaces for underscore and then make it lower case, if it's not true, just replace the spaces for underscore without making it lower case
function slugify (sentence, lowercaseBoolean) {
if (lowercaseBoolean) {
return sentence.replace(/s/g, '_').toLowerCase();
}
return sentence.replace(/s/g, '_');
}
Return means return a value from a function and STOP that function from running, so it's possible to write it without else in the example above
Another way for the example above:
function slugify (sentence, boolean) {
let slug = sentence.replace(/s/g, "_"); // Replace the spaces first
if (boolean) {
return slug.toLowerCase(); // If true, lowercase it and returns it
}
return slug; // If false, just returns the sentence replaced
}
First for the equals
1 equal means assigning something cat = 10
means we are giving cat the value of 10, so it won't give us true or false
2 equals means checking if is the same value and returns true or false cat == 10
checks if a variable or function named cat is equal to 10, this would return true if is the case
3 equals means check if is the same value and the same type cat === 10
would return true, the variable cat contains a number, which makes the variable cat to be a number type, and that comparision is checking the type too. cat === "10"
would return false since "10" with quotes would be a string and cat a number
This helps to also check if a variable is null
or undefined
. if (cat === null) {}
Bang !
makes it the opposite, !=
means not equal to
10 >= 10
returns true
All values are truthy unless they are defined as falsy
Examples:
if(true) {
console.log("cat") //True, prints cat
}
if(42) {
console.log("I like pizza") //true, prints I like pizza
}
//or....
if("0")
if([])
if("false")
if(-Infinity)
if(-3.14)
All of this returns true
Values considered false, declared as:
if (false) {
// Not reachable
}
if (null) {
// Not reachable
}
if (undefined) {
// Not reachable
}
if (0) {
// Not reachable
}
if (-0) {
// Not reachable
}
if (0n) {
// Not reachable
}
if (NaN) {
// Not reachable
}
if ("") {
// Not reachable
}
To represent a condition that has two or more elements to be true or false, we use &&
If cat is 10, and cat2 is 20, and name is "kitty" do this, if either of those variables are not what they are supposed, that part of the code will be skipped
const cat = 10;
const cat2 = 20;
const name = "kitty"
if (cat === 10 && cat2 === 20 && name === "kitty") {
console.log("This is true")
}
The same way for OR, but instead && we use ||
. If any of those variables is true, the code will be run
const cat = 2000;
const cat2 = 20;
const name = "paw"
if (cat === 10 || cat2 === 20 || name === "kitty") {
console.log("This is true")
} //prints since cat2 is true, even though the rest are not
We can use both together
const name = "john"
const last = "doe"
if (name === "frank" || (name === "john" && last === "doe"){
console.log("Great name")
}
If the first operand is truthy, "&&" returns the second operand
true && "dog"; //returns "dog"
const mode = "danger"
const check = "dangerous".includes(mode);
if (check) {
console.log("Angry cat")
}
Also works with functions
function isAwesome(name) {
return "awesome".includes(name);
}
if (isAwesome("some")){
console.log("it works")
}
To convert a truthy or falsy value into a real boolean !!
double bang is used, for example a variable that has a string in it is a string, but with !!
would convert into a true
const empty = "";
if (empty) {
console.log("this won't be printed");
}
if (!empty) {
console.log("This will be printed"); //Bang makes it opposite, its true
}
if (!!empty) {
console.log("Won't be printed since is a real false")
}
Short way for writing a simple if else statement, we use ?
as the condition and :
as else, to represent else as nothing we use null
after the :
1- Condition
2- What to do if true
3- What to do if false
const isCute = true;
isCute ? console.log("How cute") : console.log("Not cute");
Other examples:
const count = 0;
const word = count === 1 ? "item" : "items";
If count is equal to one, give to the word variable the value of "item", if it's not one, word would contain "items"
We can use then after
const count = 1;
const word = count === 1 ? "item" : "items";
const sentence = `You have ${count} ${word} on your cart`;
console.log(sentence); //print "1 item"
const count = 28;
const word = count === 1 ? "item" : "items";
const sentence = `You have ${count} ${word} on your cart`;
console.log(sentence); //print "28 items"
It's possible to push even forward by doing it in the curly brackets
const count = 1;
const sentence = `You have ${count} item${count === 1 ? "" : "s"} on
your cart`;
console.log(sentence);
Or functions
const isAdmin = true;
function showAdminBar() {
console.log("Showing admin bar")
}
isAdmin ? showAdminBar : null;
When checking a number of functions in a if statement, if one is false the rest won't be activated
function check1() {
console.log("hi 1")
return true;
}
function check2() {
console.log("hi 2")
return true;
}
function check3() {
console.log("hi 3")
return true;
}
if (check1 === true && check2 === true && check3 === true) {
console.log("All true")
} else {
console.log("Some checks failed")
}
If one of them is false, the rest won't be executed, so there's a trick to abuse this by using &&
const isAdmin = true;
function showAdminBar() {
console.log("Showing admin bar")
}
isAdmin && showAdminBar();
This checks if isAdmin is true, if it's true it will run the function, if it's false it won't run
Other way of typing if:
isAdmin = true;
function showAdminBar() {
console.log("Showing admin bar")
}
if (isAdmin) { showAdminBar() };
The switch
statement evaluates an expression, matching the expression's value against a series of case
clauses, and executes statements after the first case
clause with a matching value, until a break
statement is encountered. The default
clause of a switch
statement will be jumped to if no case
matches the expression's value.
const x = 0;
const y = 0;
function handleKeyDown (event) {
if (!event.key.includes("arrow")) return;
switch (event.key) {
case "arrowDown":
y++
break;
case "arrowRight":
x++
break;
default:
console.log("Not valid")
break;
}
}
setTimeout()
is a method that takes two arguments, the callback and how many miliseconds it needs to wait in order to run the callback
setTimeout(callback, ms)
setTimeout(function(){
console.log("It's time")
}, 2000);
It's called a callback because when the code is read, it sets the timer and keep going down the code, then when the timer is off it comes back to run that
If you need to stop the timer you can use clearTimeout()
function compliment() {
console.log("You are cute!");
}
const cuteTimer = setTimeout(compliment, 2000)
window.addEventListener("click", function (){
console.log("Stopping compliment")
clearTimeout(cuteTimer);
})
By clicking the window you'd stop the timer
Works the same but with how often you want to run the code in miliseconds
setInterval(callback, timesEachMs)
function cat () {
console.log("I like cats")
}
setInterval(cat, 100); //Will run one time every 100 miliseconds
To stop the intervals works the same, by using clearInterval()
Everything in JavaScript is an object. Objects are for storing related data, storing functionality or creating your own custom types
Allows us to group together properties
and values
The order of the properties does not matter
Properties can have spaces, dashes or start with numbers, unlike variables
Is separated by commas
Can include nested properties/nested objects
Note: The examples below are creating an object using a constant, the values inside the object can change, this is called mutation. But we can't reasign a whole block
{...}
of an object since it's a constant
To create an object you open and close a curly bracket, known as objetc literal syntax {}
const age = 100;
const person = {
age,
name: "uwu",
"777": 28,
"Hello how are you": true,
}
console.log(person);
The values can be any type
If the variable and the propertie have the same name, it can be like this to avoid redundance. This is the same as writing age = age,
:
const age = 100;
const person = {
age,
name = "John"
}
Properties that are objects or have more properties to them
const person = {
name: "Susan",
age: 425,
dog: {
cute: true,
"dog-name": "paw",
"dog-age": 11,
},
cute: false,
};
console.log(person);
You can add, change or delete values to an object by using dot notation
const person = {
name: "Susan",
age: 425,
dog: {
cute: true,
"dog-name": "paw",
"dog-age": 11,
},
cute: false,
};
person.job = "web developer"; //Adding propertie
person.age = 12; //Changing value
delete person.cute // Deletes that property and returns true/false
If you wanna make an inmutable object we can use Object.freeze()
this create an inmutable version of that object
const personFreeze = Object.freeze(person);
Used to access a property, similar to the dot notation used all above, it's used as square brackets in order to place variables there, or strings with invalid properties names like "Hello how are you": true;
const person = {
name: "Susan",
age: 425,
dog: {
cute: true,
"dog-name": "paw",
"dog-age": 11,
},
cute: false,
};
console.log(person["name"]);
Example of using square brackets vs dot notation
const person = {
name: "Susan",
age: 425,
dog: {
cute: true,
"name": "paw",
"age": 11,
},
cute: false,
};
const propertyToCheck = prompt("What property do you want to check?");
console.log(propertyToCheck);
console.log(person[propertyToCheck]);
You can also use dot notation to access nested objects
person.name //returns "Susan"
person.dog.name //returns "paw"
As explained some time ago, methods are functions that are inside an object. To create one we create a function as a propertie
const person = {
name: "Susan",
age: 425,
sayHello: function (greeting = "hey") {
return `${greeting} ${this.name}`;
},
};
To use this method we can use person.sayHello()
and it would return "hey Susan" depending on the value we give in the argument
If you look at a method and you look to the left of the dot, in the example above this
would be equal to "person", is used to to be able to use the methods on other objects
You can also create a method without function
, removing it and removing the colons
const person = {
name: "Susan",
age: 425,
sayHello(greeting = "hey") {
return `${greeting} ${this.name}`;
},
}; //Accesed by typing person.sayHello()
It's also possible but you won't have access to the this
keyword
const person = {
name: "Susan",
age: 425,
sayHi: () => {
console.log("Hi!");
}
}; //Accesed by typing person.sayHi()
When comparing objects, it references to the object itself, not the values inside of it. This means that even if two objects have the exact same properties and values, it won't be true when comparing
let cat = "cat";
let cat2 = "cat";
cat === cat2 ? console.log("cat true") : console.log("No");
//prints "cat true" since the value in the variable is the same
const catObj = {
hair: true,
cute: true,
gender: "Male",
}
const catObj2 = {
hair: true,
cute: true,
gender: "Male",
}
console.log(catObj === catObj2);
//prints false since it's comparing the object, not the values
When using variables, we can update a variable by making a copy of other one like this
let cat = "cat";
let cat2 = "notACat"
cat2 = cat;
console.log(cat2);
This changed cat2
variable to be "cat" using the first cat
variable
But when using objects doing the same thing could lead to bugs , since it does not create a copy of the other one, instead it references the first object
const catObj = {
hair: true,
cute: true,
gender: "Male",
}
const catObj2 = catObj;
This might not seem like a problem at first, but if we change values inside the new object created they will change in the first object too
const catObj = {
hair: true,
cute: true,
gender: "Male",
}
console.log(catObj); // object 1 hair value is true here
const catObj2 = catObj; // creating object 2 "with same values as obj 1"
catObj2.hair = false; // hair value changed to false in object 2
console.log(catObj2); // hair value is false
console.log(catObj); // hair value is false without updating object 1
This can lead into bugs, since when updating object 2, we also changed object 1 without wanting to do it. This is because it's a reference
To avoid the problem above, we use something called spread
Spread is the three dot operator and is used to take every item inside and object and spread it into another
const catObj = {
hair: true,
cute: true,
gender: "Male",
}
const catObj2 = { ...catObj};
It's possible to spread from two objects into one or add new ones
const pizzaIngredients = {
tomato: true,
cheese: true,
pineapple: false,
}
const hamburguer = {
beef: true,
lettuce: true,
onion: "never",
}
const inventory = { ...pizzaIngredients, ...hamburguer, bread: 10};
console.log(inventory);
Functions inside objects also might overwrite something when passing an object to them and modyfing that object, the external will also be updated. So you need to make sure to pass it as a copy
But when doing nested objects, we run into the same problem
Spread is a shallow copy, only copy the first layer is copied, if we have another object we run into the exact same problem from above, to avoid this we need to use a utility library
Lodash
Using Lodash
library we can create a deep copy of an object by using the _.cloneDeep()
method
const catObj = {
hair: true,
cute: true,
gender: "Female",
kitties: {
"Has kitties": true,
number: 7,
},
}
console.log(catObj.kitties["Has kitties"]) // true
const catObj2 = _.cloneDeep(catObj); // cloning obj1 into the new obj2
catObj2.kitties["Has kitties"] = false; // Making an obj2 value false
console.log(catObj2.kitties["Has kitties"]) // false, obj2 value is false
console.log(catObj.kitties["Has kitties"]) // true, obj1 was not changed
Is a collection of data identified by keys, the main difference to an object is that map
allows keys of any type.
The order here matters, and is another reason why using maps and not objects
Can place an object as the key and add a description for that object
Methods and properties are:
new Map()
- creates the mapmap.set(key, value)
sets a value specified for a keymap.get(key)
- Returns the key value. Undefined if it does not existmap.has(key)
- returns true if the key exist in the map, false
if it doesn'tmap.delete(key)
- Removes an element with that keymap.clear()
- Removes everything from the mapmap.size
- Size, returns the actual quantity of elementsCreating a map with the keys we want in the way we want, then we give them values.
It's possible to also include an object as a key, and give it a value too:
const person1 = {
name: "John",
age: 2412,
}
const myMap = new Map();
myMap.set("name", "Husk");
myMap.set(100, "This is a number");
myMap.set(person1, `${person1.name} is really cool`)
Another example:
let score = 500;
const prizes = new Map();
prizes.set(100, "old CD");
prizes.set(300, "teddy bear");
prizes.set(500, "amazing cat plushie");
console.log(`You won a ${prizes.get(score)}`);
// "You won a Amazing cat plushie"
Example with an if
to practice and make the syntaxis correct
If the score is 100, or 500, add "n" for it to be correct: "an old CD...", "an amazing cat..."
let score = 500;
const prizes = new Map();
prizes.set(100, "old CD");
prizes.set(300, "teddy bear");
prizes.set(500, "amazing cat plushie");
console.log(`You won a${score === 100 || score === 500 ? "n" : ""} ${prizes.get(score)}`);
Arrays are similar to a list where we can do
The Array
object, as with arrays in other programming languages, enables storing a collection of multiple items under a single variable name, and has members for performing common array operations.
item
- Each thing inside an array
index
- Position in the array of an item
length
- Number of items inside an array
Each item can be any type
When using
typeof
on an array, you'll get object, because arrays don't have their own type, to check if something is an array use theArray.isArray()
method, returns true or false
To create an array we use Square Brackets []
this is known as an array literal
const names = ["Paw", "Moon", "Appa"];
We can also create one by this
const names = new Array();
To access an item of an object, we use its index number
const names = ["Paw", "Moon", "Appa"];
console.log(names[0]); // Paw
console.log(names[1]); // Moon
console.log(names[2]); // Appa
console.log(names.length); // 3
It's really important to know that arrays start from 0, not from 1, so in order to get the first item we access using 0
To access for example the last item of a large array, we can say
const names = ["Paw", "Moon", "Appa", "Paw", "Moon", "Appa", "Paw", "Moon", "Appa", "Paw", "Moon", "Appa", "Paw", "Moon", "Appa", "Paw", "Moon", "Appa", "Paw", "Moon", "Appa",];
console.log(names[names.length- 1]); // Appa
Because names.length
would have a value of 3, but we can't access 3 since the last index is 2, because it starts in 0
Mutation refers to when something changes the original version of it
Immutable refers to the methods not chanting the original version, instead return a new array
Some methods will modify the original array when some will create a new array and leave the original as it.
Note: For react, is a good to when using React, don't change the original version, instead create a copy
.slice()
method takes as argument the element where you start and the element where it ends without counting that last one
// Mutation method
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9];
numbers.reverse(); // Reverts the data, 9,8,7...
console.log(numbers); // The original was changed, 9,8,7,6...
// Immutable
const slice = numbers.slice(2, 4); //Took a slice of an array out
console.log(slice); // [3, 4]
console.log(numbers); // 1,2,3... Wasn't changedd
In order to use a Mutable Method but keeping the original array as it is, we need to Spread to first create a copy, then edit that one
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]; // First array
const numbersReversed = [...numbers]; // Create a copy
numbersReversed.reverse(); // Change the values of the copy
console.log(numbersReversed); // [9,8,7,6...] changed
console.log(numbers); // [1,2,3,4...] wasn't changed
You can also do this:
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]; // First array
const numbersReversed = [...numbers].reverse(); // Create a copy
.push()
- Add an item to the array, in last positionconst languages = ["Hello", "JavaScript", "Python", ];
const newLanguages = [ ...languages, "C", "Java"] //Make a copy and push C and Java
newLanguages.push("C++");
console.log(newLanguages); //['Hello', 'JavaScript', 'Python', 'C', 'Java', 'C++'
.unshift()
- Add an item, in first position
.pop()
- Deletes the last item in the array
.shift()
- Deletes the first item
.findIndex()
- Used to find a particular element in an array, loops over until it finds it and returns true. There's a longer way to do it that makes a bit more sense.
1st it loops over how many items are in the array, then takes a callback function and that checks if the item is the same, if it is it returns true and gives the position, if false it skips to next one
const names = ["John", "Samuel", "Yeseo", "Juan", "Hello"];
const samuelIndex = names.findIndex(name => name === "Samuel"); //true
console.log(samuelIndex); // 1
console.log(names[samuelIndex]); // Samuel
//Larger version
names.findIndex(function (name) {
if (name === "Yeseo") {
return true;
} return false;
}); // 2
.includes()
- Checks wether the array contains the specified element
.concat()
- Join two different arrays from end to start
const firstArray = ["Butterflies", "Moonlight", "Sunrise",];
const secondArray = ["Cat"]
const bothArrays = firstArray.concat(secondArray);
console.log(bothArrays); // ['Butterflies', 'Moonlight', 'Sunrise', 'Cat']
.forEach()
- Runs a loop once for each element in the arrayconst newArray = ["Cat", "John", "Angel"];
newArray.forEach(function (element) {
console.log(element); // Cat, John, Angel
});
.sort()
Sorts in alphabetical order in ascending order
.splice(start[,deleteCount])
- Deletes existing methods and allows to add too. First argument is where it starts, the second argument is how many it will delete from there
const numbers = [1,2,3,4,5,6,7,8,9,];
numbers.splice(3, 4);
console.log(numbers); //[1,2,3,8,9] started from 3 and deleted 4 from there
.slice()
method takes as argument the element where you start and the element where it ends without counting that last oneUsing slice we place the positions of the items we want, then add a new one, then slice again to add the rest
const languages = ["Hello", "JavaScript", "Python", "C", "Java",];
const newLanguages = [
...languages.slice(0,2), // Adds "Hello" and "JavaScript
"C++", // Adds "c++"
"SQL",
...languages.slice(2), // Adds the rest starting from 2
];
console.log(newLanguages);
To remove an item we can simply make a copy without it
const languages = ["Hello", "JavaScript", "Python", "C", "Java",];
const newLanguages2 =[
...languages.slice(0,2),
...languages.slice(3),
]
In a larger array or when using React is pretty common to
const names = ["John", "Samuel", "Yeseo", "Juan", "Hello"];
const yeseoIndex = names.findIndex(name => name === "Yeseo");
const namesWithoutYeseo = [
...names.slice(0, yeseoIndex), //from 0 to the Yeseo index
...names.slice(yeseoIndex+1) // From Yeseo index + 1, until it ends
];
console.log(namesWithoutYeseo);
Static methods are for creating or working with arrays, they
Array.of()
Used to create an array
Array.of("John", "Doe",)
//Returns an array with those items
Array.from()
Creates an array with empty or undefined slots first argument takes an object with the length. The second argument takes a map function to tell us what can go inside those slots
const empty = Array.from({length: 10});
console.log(empty);
const range = Array.from({length: 10}, function () { //Create array 10 empty slots
return "Hello"; // Fills the slots with the return
});
Or get the index the same way, we place an underscore to reference that that argument will not do anything, but it needs to be placed since that map function takes two arguments
const range = Array.from({length: 10}, function (_item, index) {
return index; //Returns
});
You can also use a function to create a range that you can decide to create an array from a number you decide to another, in example 4-100 would create an array that goes from 4 to 100
function createRange(start, end){
const range = Array.from({length: end - start + 1},
function (_item, index) {
return index;
});
return range;
}
Array.isArray()
Usually we use typeof
but an array is an object type so we would get an object, in order to check if an array is an array, we need to use the Array.isArray()
method
const rangeArray = createRange(4,100); // Following the previous examp.
console.log(Array.isArray(rangeArray)); // True
Object.keys()
Object.values()
Object.entries()
Used to get keys, values or both of an object and place it in arrays
const meatValues = Object.values(meats); // Array with object values
const meatKeys = Object.keys(meats); // Array with object keys
const meatEntries = Object.entries(meats); // Array with both
.join()
Separates the items of an array into a string
const buns = ['egg', 'wonder', 'brioche'];
console.log(buns.join(" or ")); // "egg or wonder or brioche"
console.log(buns.join(" - ")); // "egg - wonder - brioche""
.split()
Takes a string and makes it an array, replacing a character with a separation for the arrays (in this example, comma)
const foodString = "hot dogs,hamburgers,sausages,corn";
const foodArray = foodString.split(","); //Split and replace comma
console.log(foodArray); //Array with "hot dogs, "hamburguers"... elements
.pop()
Takes the last item of an array, but it returns that item, so it can be stored to be used later
.push()
Place an item as the last one inside an array. Returns the updated number of items after adding that last item
const toppings = ['Mushrooms ', 'Tomatoes', 'Eggs', 'Chili', 'Lettuce', 'Avocado', 'Chiles', 'Bacon', 'Pickles', 'Onions', 'Cheese'];
const lastItem = toppings.pop(); //Takes the last item, mutating the array
topping.push(lastItem); //place the item again since was stored "lastItem"
console.log(topping.push(lastItem)); // 11, returns the number of items
.shift()
Takes the first item of an array, returns that item
.unshift()
Places an item as first in the array. Returns the updated number, same example as above
Example adding and removing items while doing it inmmutable.
Some examples and exercises with arrays:
let toppings2 = toppings.slice(0, toppings.length - 1); //Remove last
toppings2 = [...toppings2, toppings[toppings.length - 1]]; //Reinsert last
toppings2 = [...toppings.slice(1, toppings.length)]; // Remove first
toppings2 = [ toppings[0], ...toppings2]; // Reinsert first
const toppingsCopy = toppings.slice(0);
//If not given second argument, goes until the end of the array
const avocadoIndex = toppings.indexOf("Avocado");
console.log(avocadoArray); //5
console.log(toppings.includes("hot sauce")); //false
if (toppings.includes("hot sauce")) {
console.log("")
} else toppings.push("hot sauce");
.reverse()
mutating the original, to not mutate create another using spreadtoppings.reverse();
console.log(toppings);
Many array methods take a callback function as an argument. The callback function is called sequentially and at most once for each element in the array, and the return value of the callback function is used to determine the return value of the method. They all share the same signature:
Take 3 arguments: element``index
, array
, and thisArg
find()
Returns the first item that satisfies the provided testing function. Takes a callback as a parameter, the callback can
const feedback = [
{ comment: "Love the burgs", rating: 4 },
{ comment: "Horrible Service", rating: 2 },
{ comment: "Smoothies are great, liked the burger too", rating: 5 },
{ comment: "Ambiance needs work", rating: 3 },
{ comment: "I DONT LIKE BURGERS", rating: 1 },
];
function findBurguer(firstFeedback) {
if(firstFeedback.comment.includes('burg')){
return true;
} else {
return false;
}
};
const burguer = feedback.find(findBurguer);
console.log(burguer);
One line way:
const burguer = feedback.find(burgs => burgs.comment.includes("burg"));
console.log(burguer);
.find will run for each element in the array until it gets a true, then will return the value of the first item that gave true, burgs
represents a parameter and can change, but it's needed to access correctly to the feedback.comment.includes("burg")
Creating a function that will take a string as an argument and then when using it we will be able to look for the word we want
function findItem(text) {
return function (singleFeedback) {
return singleFeedback.comment.includes(text);
};
}
const aokay = feedback.find(findItem("burg"));
const aokay2 = feedback.find(findItem("Smooth"));
Functions like
find()
andfilter()
it's really helpful to name them extremely explicit, because many times you will use them in other files
.filter()
Loops every element and returns true or false
const ratingsAbove2 = feedback.filter(itemOfTheArray => {
if (itemOfTheArray.rating >= 2){
return true;
} else {
return false;
}
});
console.table(ratingsAbove2);
Compact way, since this if only evaluates if it's true or false, removing the return true and return false, and just returning the evaluation
const ratingsAbove2 = feedback.filter(itemOfTheArray => {
return itemOfTheArray.rating >= 2;
});
console.table(ratingsAbove2);
Or another way that would be more usual, a single line
const ratingsAbove2 = feedback.filter(itemOfTheArray => itemOfTheArray.rating >= 2);
console.table(ratingsAbove2);
The same example of find but using filter:
const ratingsBurguer = feedback.filter(item => item.comment.includes("burg"));
console.table(ratingsBurguer);
Removing an element or elements using find or filter
const ratingsLow = feedback.filter(itemOfTheArray => itemOfTheArray.rating < 2);
console.table(ratingsLow); //Shows the items below rating 2
feedback.pop(ratingsLow); // Removes them
console.log(feedback); // Feedback mutated and now does not include
.some()
Tests wether at least one element
in the array passes the test implement by the provided function, returns true or false
const meats = {
beyond: 10,
beef: 5,
pork: 7,
};
const isThereEnoughOfOneTypeOfMeat = Object.values(meats).some(meatQuantity => meatQuantity >= 5);
console.log(isThereEnoughOfOneTypeOfMeat); //true
.every()
Makes sure every single element in the array meets our criteria
const isThereEnoughEveryMeat = Object.values(meats).every(meatQuantity => meatQuantity >= 3);
console.log(isThereEnoughEveryMeat);
.sort()
Sorts elements of an array, converts it into strings and compare their sequences of UTF-16 code units values (alphabetically)
Takes as argument a callback function to compare and decide wether something will be in front or something or behind
const numbers = [1,2,5,100,500,242,900,1952,7,4,2];
const sortNumbers = numbers.sort(function (firstItem, secondItem){
return firstItem-secondItem;
});
Most common is looping over an array, does not return anything
.forEach()
Loops and runs the callback function for each element in the array, object or variable. Takes:
callback
function as a parameter
currentValue
- The reason we need to give an argument inside, is because this translates into the currentValue
of the array, if we name this parameter as item
or whateverIDK
, it still means the value of the array. In example if we have an array const myArray = [1,4,5,"cat"];
when using that parameter we named before, it has the value of 1, then 4, then 5, then "cat"
`index`` - (optional) Gives the index number of the element
array
` - (optional) Gives the complete original array
const toppings = [
"Mushrooms ",
"Tomatoes",
"Eggs",
"Chili",
"Lettuce",
"Avocado",
"Chiles",
"Bacon",
"Pickles",
"Onions",
"Cheese",
];
function logToppings(currentTopping) {
console.log(currentTopping)
}
toppings.forEach(logToppings);
Side effect is when you are in the scope of a function and you reach outside to do something else, like attaching an event listener or put something on the page
Creates a new array with the results provided by a function Takes in data, performs an operation and gives it on the other side, always produce the same length of the array
const faces = ['😃', '🤠', '🤡', '🤑', '😵', '🌞', '🐶', '😺'];
function addArms(face) {
return `🤚 ${face} 🤚`
}
const toys = faces.map(addArms);
console.table(toys); //A Table wich each face but with added hands
or
const familyDoe = ["John", "Johana", "Karina", "Winter"].map(itemValue => `${itemValue} Doe`)
Same example but capitalizing a letter using two maps together
function capitalize(item) {
return `${item[0].toUpperCase()}${item.slice(1)};`
}
const familyDoe = ["John", "johana", "Karina", "winter"].map(capitalize).map(itemValue => `${itemValue} Doe`)
So map will always take in an item, do some work with it and return the new value
More map examples
const orderTotals = [342, 1002, 523, 34, 634, 854, 1644, 2222];
const orderTotalsChange = orderTotals.map(total => 1);
//returns an array with 1,1,1,1,1,1,1 since it replaces the element of
// the array with the return of the map
Map can also be used with any type of data. You will more likely get objects that come from an API
Since they come from an API, not always they will be in the format that we want, so we can change it
const people = [
{
birthday: 'April 22, 1993',
names: {
first: 'Keith',
last: 'Buckley'
}
},
{
birthday: 'January 3, 1975',
names: {
first: 'Larry',
last: 'Heep'
}
},
{
birthday: 'February 12, 1944',
names: {
first: 'Linda',
last: 'Bermeer'
}
}
];
const cleanPeople = people.map(function cleanse(person){
const birthday = new Date(person.birthday).getTime();
const now = Date.now();
const age = Math.floor((now - birthday) / 31536000000);
return {
age, // Short of: `age = age,`
name: `${person.names.first} ${person.names.last}`
}
});
console.log(cleanPeople);
Since the dates were strings and the names were separated, we converted the string and date into a more real date
Creates a shallow copy of a portion of a given array, checks every item in the array if it's true it goes into the copy, if it's false it doesn't
Parameters:
callback function
element
index
array
const words = ['spray', 'elite', 'exuberant', 'destruction', 'present'];
const result = words.filter(word => word.length > 6);
//Only words with more than 6 letters
.find()
To find an item, returns an object
const students = [
{
id: '11ce',
first_name: 'Dall',
last_name: 'Puckring',
},
{
id: '2958',
first_name: 'Margarete',
last_name: 'Brandi',
},
{
id: '565a',
first_name: 'Bendicty',
last_name: 'Woodage',
},
];
const margarete = students.find(studentID => studentID.id === '2958');
Function to find properties of an object using find:
function findByProp(prop, propWeAreLookingFor) {
return function (item){
return item[prop] === propWeAreLookingFor;
}
};
const student1 = students.find(findByProp("id", "2958"));
const student2 = students.find(findByProp("last_name", "Woodage"));
console.log(student1);
console.log(student2);
We create a function that takes two argument, the key and the value, and then we compare if the item we are looking corresponds to the property we game. We used square brackets since it is a property
Takes an amount of data and returns a result or a single value. Takes two arguments:
callbackFunction
accumulator
(Value result of the previous call to callbackFn
)
currentValue
(The value of the current element)
initialValue
(optional, a value which accumulator is initialized)
Syntax:
reduce(callbackFn, initialValue);
const orderTotals = [342, 1002, 523, 34, 634, 854, 1644, 2222];
function tally(tally, currentPrice){ //accumulator and current value
return tally + currentPrice;
}
const tallyTotal = orderTotals.reduce(tally , 0); //0 is initial value
console.log(tallyTotal);
The callback function takes an accumulator and the currentValue,
Creates a loop that consist in three optional expressions, enclosed with a parenthesis, followed by a statement to be excecuted in each loop
Syntax
for(initialization; condition; afterthought) {statement}
Basic for example
for(let i = 0; i <= 10; i++) {
console.log(i);
}
For with arrays
const numbers = [1,4,5,2,5,63,78,0];
for(let i = 0; i < numbers.length; i++) {
console.log(numbers[i]);
}
Used for looping over iterables, it can handle emojis. If you need to sequence data, one thing another in a loop, for of is very handy for sequency promises
Iterables are something that has a length
const name = "Cat";
for(const letter of name) {
console.log(letter);
} //
Used for looping over keys of an object
const guy = {
name: John,
age: 19,
"okay": true,
}
for(const property in guy) {
console.log(property); //name, age, "okay"
}
Will take a condition and run infinitely until the condition is false, this can crash the tab or navegator, so yo need an exit condition
let cool = true;
let i = 1;
while (cool === true) {
console.log("you are cool");
i++
if(i > 100) {
cool = false; //Exit condition
}
}
Similar but excecutes the code at least once, since the condition is read after excecuting the statement
Important notes for this section:
A function that makes an object is called a
constructor
Inheritance
andinstances
refers to passing down characteristics from a parent to a child so that a new piece of code can reuse and build upon the features of an existing one
this
refers to the instance of the thing that was made. this
refers to the object that the function is accessed on and changes for every function or scope its in, for exampleconst button1 = document.querySelector(".button-one");
function tellMeAboutTheButton() {
console.log(this); // will print the <button> tag of button1
}
button1.addEventListener("click", tellMeAboutTheButton);
// 'this' will refere to button1, the object in which the function is
// accesed on
this
is also used to store information inside that instance
// Using constructor to create a template object
// This object takes two arguments as parameters, an array of toppings and a customer
function Pizza(toppings = [], customer) {
console.log(this); // Pizza object
// Store information from the argument into the object Pizza
this.toppings = toppings;
//this.customer creates the template, customer is the argument we are getting the info from
this.customer = customer;
// Just random ID
this.id = '#' + Math.floor(Math.random() * 16777215).toString(16);
}
//Creating an instance of Pizza, using different arguments
const pepperoniPizza = new Pizza(["Pepperoni", "Cheese,", "Tomato Sauce"], "John Doe");
const hawaianaPizza = new Pizza(["Pineapple", "Cheese,", "Tomato Sauce"], "Jane Pineapple Doe, the lover of pineapple pizza");
pepperoniPizza and hawaianaPizza are an instance of
'Pizza' and they have their own unique information.
When this
is invoked as a standalone function (not attached to an object: func()), it will refer to the global object
this
inherits from the parent scope at the time they are defined, this is useful for callbacksArrow example to change the button content using this
const button1 = document.querySelector(".button-one");
function changeButtonText() {
console.log(this); // <button class="button-one">Test</button>
setTimeout(() => {
this.textContent = `Button clicked`; // 'this' still refers to the button
}, 1000); //Changed the button content from 'Test' to 'Button clicked'
}
button1.addEventListener('click', changeButtonText)
Because we are using an arrow function, the this inside that will still refer to the button1 tag, if we hadn't used an arrow and made a normal function, this would've changed and it would not work
The new
operator lets developers create an instance of a user-defined object type or of one of the built-in object types that has a constructor function.
Syntax
new Constructor()
We use new
here to create other instances from the parent Object Car
and store it in another variable
// Blueprint for other Objects with this same properties
function Car(make, model, year) {
this.make = make;
this.model = model;
this.year = year;
}
//'new' here creates a new instance of the Object Car, with the properties
const car1 = new Car('Eagle', 'Talon TSi', 1993);
const car2 = new Car('idk', 'Ferrari yes', 2018)
console.log(car1);
// Car {make: 'Eagle', model: 'Talon TSi', year: 1993}
console.log(car2);
// Car {make: 'idk', model: 'Ferrari yes', year: 2018}
a
Every object in JavaScript has a built-in property, which is called its prototype
This is an object with one data property:
const myObject = {
city: "Canada",
greet() {
console.log(`welcome to ${this.city}`);
},
};
myObject.greet(); // welcome to Canada
If we type myObject
in the console, we will be able to access to a list of all properties availabe for this object, besides the ones we created, for example toString()
When we call myObject.toString()
, the browser:
toString
in myObject
myObject
for toString
Each type of data shares different functionalities in the protoype, for example all arrays share the .filter
method
It's useful to create a function which is stored in prototype, because when we use a function inside an object we are creating a copy everytime, that's fine for some objects and methods, but when we are dealing with creating 20,000 pizzas it will be slower
Object.prototype.functName
Creating a function of eat following the same example of the this
section
function Pizza(toppings = [], customer) {
console.log(this);
this.toppings = toppings;
this.customer = customer;
this.id = "#" + Math.floor(Math.random() * 16777215).toString(16);
this.slices = 10;
}
// Creating prototype level function
Pizza.prototype.eat = function () {
if (this.slices > 0) {
//'this' is the Pizza object, accessing to slices
this.slices = this.slices - 1; // Removes one slice
console.log(`Ñom Ñom`);
return `${this.slices} left`; // Slices left
}
return "No more pizza"; // When slices is 0, no more pizza is returned
};
const pepperoniPizza = new Pizza(["Pepperoni", "Cheese,", "Tomato Sauce"],"John Doe");
const hawaianaPizza = new Pizza(["Pineapple", "Cheese,", "Tomato Sauce"],"Jane Pineapple Doe, the lover of pineapple pizza");
This way this function is created only once and lives in the proto
So when using the Object Pizza 20,000 times, it won't create a function and run it each time, we can access it in the console or our code editor by typing:
pepperoniPizza.eat();
Like if we use any other proto function before
Added in 2016 to browsers so it's a bit new
classes
are a template for creating objects. They encapsulate data with code to work on that data
// Declaration
class Rectangle {
constructor(height, width) { //Parameters of what will take as arguments
this.height = height;
this.width = width;
}
} // classes don't use semicolon... For some reason
Following the same example above, classes work similar to the Objects
class Pizza {
constructor(ingredientes=[], customer) {
this.ingredientes = ingredientes;
this.customer = customer;
}
}
const meatPizza = new Pizza(["meat", "another kind of meat"], "MeatLover3000");
Common for interviews
They change the scope of what this
keyword is equal to, inside of a function
bind()
this
is always defined where the function is being called and NOT where it was defined, to change this we can use bind
const whiteCat = {
isWhite: true,
age: 1000,
name: "baby cat",
bite() { // Method of whiteCat, `this` here is whiteCat
return `You got bit by ${this.name}`;
}
};
const angryDog = { name: "angry dog"}
// Bind 'this' from the bite methond inside of whiteCat, into angryDog object
const bite = whiteCat.bite.bind(angryDog);
Usually you can't just do something like store document.querySelector()
in a variable, and then reuse that variable as a replacement from the querySelector
const $ = document.querySelector;
console.log($("body")); // Error: Illegal invocation
That's because querySelector needs to know where to look inside of in order to find the element we are looking for, it knows where to look because it uses this as the document
We can use bind to change the this in order to be able to do it
// Manually change this to "document"
// By using bind against querySelector, we say when the function $
// is run, use `document` as the `this` value
const $ = document.querySelector.bind(document);
// Store the document.querySelector in a variable
const paragraph = $("p");
console.log(paragraph);
This way we made a shorter way to do document.querySelector()
by only writting $()
Useful to creat libraries that has shortcuts, we can instead of saving it into a variable, we can save it into a function a do some more changes inside that function
call()
Does the same thing as bind
, but it aditionally runs the function
apply()
The same as call, but it takes an array as argument
Full example of this
const prices = {
total: 1000,
calculateTax (taxRate) {
return this.total + (this.total * taxRate)
},
totalMeal (mealType, drinkType, taxRate) {
return `You bought ${mealType} and ${drinkType}, your total is ${this.calculateTax(taxRate)}`
}
};
const price1 = prices.calculateTax.bind(prices);
console.log(price1(0.15));
// we can manually add an object, and the second argument will be taken as the argument
const price2 = prices.calculateTax.call({total: 500}, 0.15);
console.log(price2); // No need to call it
const newPrice = {
total: 5000,
};
// Same but using a single array as argument
const price3 = prices.calculateTax.apply(newPrice, [0.32]);
// Calculate totalMeal, prices is needed to reference "this" and the array for the other parameters
const bigMeal = prices.totalMeal.apply(prices, ["pasta", "expensive water", 0.15])
console.log(bigMeal);
Is caused when using many callbacks one after another
When we use something like setTimeout()
the programm waits for those seconds in something called "Web API", if we use many callbacks or timeouts nested
const div = document.querySelector(".go");
function clicked () {
div.textContent = "Go";
setTimeout(() => {
div.classList.add("circle");
setTimeout(() => {
div.classList.add("red");
setTimeout(() => {
div.classList.remove("circle");
setTimeout(() => {
div.classList.remove("red");
div.classList.add("purple");
setTimeout(() => {
div.classList.add("fade");
}, 500);
}, 300);
}, 250);
}, 500);
}, 2000);
}
div.addEventListener("click", clicked);
The Promise
object represents the eventual completion (or failure) of an asynchronous operation and its resulting value.
For example a data being returned from an API or someone giving access to a webcam or a microphone in the website. What we often get when we request those things is not the immediate data back, instead we get a Promise
. A promise of maybe getting data, or not getting it which is called Rejecting
Used with Async
and Await
When you mark a function with async, that function will return a promise
Takes a callback with two arguments: resolve function and reject function
Promise(function(resolve, reject) {}):
Example for creating a Pizza when getting true from a fake outside
let someAPIData = true;
function makePizza(toppings) {
// Promise of getting something
const promisedPizza = new Promise(function (resolve, reject) {
// If we get true from the api data, resolve this promise after .9 sec
if(someAPIData) {
// Resolve
setTimeout(() => {
resolve(console.log(`Pizza made with the toppings: ${toppings}`));
}, 900);
// If we don't, reject
} else {
// Reject
reject(console.log("Failed to obtain confirmation"));
}
});
return promisedPizza;
}
The .then()
method takes up to two arguments; the first argument is a callback function for the fulfilled case of the promise, and the second argument is a callback function for the rejected case. Each .then()
returns a newly generated promise object, which can optionally be used for chaining
function makePizza2(toppings) {
// Short way of returning the new Promise object
return new Promise(function (resolve, reject) {
//Resolve
setTimeout(() => {
// Using join to be able to take an array of toppings
resolve(`Here's your pizza with the toppings: ${toppings.join(" ")}`);
}, 1400);
//Reject
});
}
makePizza2(["Pepperoni"])
.then(function (resolvedCase) {
console.log(resolvedCase);
return makePizza2(["cheese", "tomato", "pineapple"]);
})
.then(function (resolvedCase) {
console.log(resolvedCase);
});
Take a closer look for the .then()
method, it takes a callback as the first argument and then returns a new promise with new arguments, in order to create a chain
Similar example but making the timeout slower for each added topping
function makePizza3 (toppings = []) { // Default empty array
return new Promise(function (resolve, reject) {
// For each topping add 200 miliseconds
const timeOfBake = 500 + (toppings.length * 200);
setTimeout(() => {
resolve(`Hi ${toppings.join(', ')}`)
}, timeOfBake);
})
}
makePizza3(["Bread", "pepperoni", "cheese", "cheese", "cheese", "cheese", "cheese", "cheese", "cheese"]).then(function(fullfiled) {
console.log(fullfiled);
})
It's possible to wait for a number of promises to be fulfilled, here we need to wait for the cheese, pepperoni and vegan pizzas to be done before sending anything, we can do it using Promise.all, which takes an array, then we can use then
to, when the promised is resolved, do something like print it
Promise.all returns an array
function makePizza3 (toppings = []) {
return new Promise(function (resolve, reject) {
const timeOfBake = 500 + (toppings.length * 200);
setTimeout(() => {
resolve(`Hi ${toppings.join(', ')}`)
}, timeOfBake);
})
}
const cheese = makePizza3(["Bread", "pepperoni", "cheese", "cheese", "cheese", "cheese", "cheese", "cheese", "cheese"]);
const pepperoni = makePizza3(["Bread", "pepperoni"]);
const veganChoice = makePizza3(["lettuce", "(I'm sorry)"]);
const allPizzas = Promise.all([cheese, pepperoni, veganChoice])
.then(finished => {
console.log(finished);
})
Similar to Promise.all()
but if one of those promises breaks or is rejected, it won't stop and will keep going
catch
It only runs when the Promise does not go succesfully, its used to handle the error of not getting a promise fullfiled, when we call a function that has a promise inside, we will use then
to do something with the fulfilled promised, and catch
for handling the error
Takes a function as an argument
function makePizza(toppings) {
// Promise of getting something
const promisedPizza = new Promise(function (resolve, reject) {
// If we get true from the api data, resolve this promise
if (someAPIData) {
// Resolve
setTimeout(() => {
resolve(`Pizza created with the toppings: ${toppings}`);
}, 900);
// If we don't, reject
} else {
// Reject
reject(console.log("Failed to obtain data"));
}
});
return promisedPizza;
}
makePizza("pepperoni")
.then(function (resolvedData) {
console.log(resolvedData);
return makePizza("Cheese");
})
.then(function (resolve) {
console.log(resolve);
}).catch(function(error) { // Place the catch at the end of the chain of thens
console.log(error);
console.log("Need access to the API Data");
})
Short way of creating a promise that will take different amounts of time
function wait(ms = 0) {
return new Promise(function(fulfill, reject) {
setTimeout(fulfill, ms);
})
}
wait(200).then(() => {
console.log("test");
})
The await
operator is used to wait for a Promise
and get its fulfillment value. It can only be used inside an async function
or at the top level of a module.
Await makes an async function wait for a promise
When using await inside a function, the cascading stops and when the promised is resolve, it continues
function wait(ms = 0) {
return new Promise(function(resolve, reject) {
setTimeout(resolve, ms);
})
}
async function go () {
console.log("1");
await wait(1000); // Wait until 1000 ms pass, then do the next line
console.log("2");
await wait(1000);
console.log("3");
};
go();
Asynchronous means that this function waiting time does not interfere with others
For await to work the function needs to be an async function
. Ways to declare async to a function for the different types of functions:
// Function declaration
async function something() {}
// Arrow function
const arrowFn = async () => {}
// Callback function
window.addEventListener("click", async function() {});
// Methods or functions inside objects
const person = {
name: "Juan",
// Method
bite: async function() {
console.log("bite");
},
// Method shorthand
async smile() {
console.log("smiling");
},
// Function property
sayHi: async () => {
console.log("hi");
}
}
Await also works to return the content of a promise instead of the promise itself
Is used to try a block of code and if it throws an error, the code in the catch will be executed. It works with any error and makes it so JavaScript does not break when it finds an error.
When it finds an error inside the try block, stops reading what's below and jumps into the catch
Syntax:
try {
// Code to be tested
} catch (exceptionVariable){ // Commonly used "error", "err", "e"
// catch statements
}
Example
try {
window.doesNotExist();
} catch(error) {
console.log(error); // TypeError: window.doesNotExist is not a function
}
// Keeps going instead of stopping there because of the error
Example using try catch blocks
function makePizza(toppings) {
// Short way of returning the new Promise object
return new Promise(function (resolve, reject) {
//Reject
if (toppings.includes("pineapple")) {
reject("No pineapple on pizza!");
}
const toppingTime = 500 + toppings.length * 200;
//Resolve
setTimeout(() => {
resolve(`Here's your pizza with the toppings: ${toppings.join(", ")}`);
}, toppingTime);
});
}
async function go() {
try {
const pizza2 = await makePizza(["pepperoni", "cheese"]);
console.log(pizza2); // Here's your pizza...
// This line will throw an error since we are adding pineapple
const pizza = await makePizza(["pineapple", "cheese"]);
// Jumps to the catch block
const anything = 10; // Is never reached
} catch (error) {
console.log(error);
}
}
go();
.catch()
can also be used similar to the previous catch() we saw, by defining a function to handle the error
// makePizza function same as above
function handleError(error) {
console.log(error);
}
async function go() {
const pizza2 = await makePizza(["pepperoni", "cheese"]);
console.log(pizza2); // Here's your pizza...
const pizza = await makePizza(["pineapple", "cheese"]);
}
go().catch(handleError);
It's a function that returns another function.
In error handling, can be used to create a function to handle the errors in a simplier way, when having to work with many functions that will throw errors
// makePizza function same as above
function handleError(error) {
console.log(error);
}
async function go() {
try {
const pizza2 = await makePizza(["pepperoni", "cheese"]);
console.log(pizza2); // Here's your pizza...
const pizza = await makePizza(["pineapple", "cheese"]);
}
catch (err) {
console.log(err);
}
}
function makeSafe(fn, errorHandler) {
return function () {
fn().catch(errorHandler);
};
}
const saferFn = makeSafe(go, handleError);
saferFn();
When people say Ajax usually they mean fetch data from an API
A way to transport JavaScript objects from server to server or server to client.
.json file:
{
"employees":[
{"firstName":"John", "lastName":"Doe"},
{"firstName":"Anna", "lastName":"Smith"},
{"firstName":"Peter", "lastName":"Jones"}
]
}
When we get a JSON on our JavaScript, we get it as a string, to make it a real object we need to save it in a variable and then parse it into JSON
const text = `{
"employees":[
{"firstName":"John", "lastName":"Doe"},
{"firstName":"Anna", "lastName":"Smith"},
{"firstName":"Peter", "lastName":"Jones"}
]
}`
// Saves those 3 objects as an array called employees
const obj = JSON.parse(text);
// Logs the first element of the array employees
console.log(obj.employees[0]);
Allows to do HTTP requests, it does not need to be from a JSON, it could be data from the text of a web page, etc. Fetch is promise-based
Steps to use fetch
Making a Request: You start by using fetch
to make a request to a URL. This returns a promise that will eventually resolve to a Response
object.
Handling Response: The first then
method is used to handle the Response
object once it's available. The Response
object does not directly contain the actual body of the response; it has a method like .json()
or .text()
to handle the data format appropriately. This first then
checks if the response is OK (status in the range 200-299). If not, it throws an error.
Using the Data: The result from the first then
(e.g., the JSON data) is passed to another then
method where you can use this data.
Error Handling: If there is an error in any part of the fetch process, such as a network error or if the response status indicates an error, you can catch these errors using a catch
block.
To understand better .then
after the fetch, here's an example
fetch('https://pokeapi.co/api/v2/pokemon') // 1 - We make a request to a URL
.then(response => response.json()) // 2 - Promise is resolved, when getting a response we transform it to a specific format
.then(data => console.log(data)) // 3 - Data is shown in the console
.catch(err => console.log(err)); // In case of an error, it'll be catched
And to access diferent properties...
First we make a request using fetch on the url, then we get the response, then we are able to manage our data and access to the different properties, then we use a catch
const pkmAPI = `https://pokeapi.co/api/v2/pokemon`;
const pkmParagraph = document.querySelector(".someParagraph");
fetch(pkmAPI)
.then((response) => response.json())
.then((data) => {
const firstPkm = data.results[0]; // Taking the first array item
console.log(firstPkm); // {name: 'bulbasaur', url: 'http://asdasd'}
const name = firstPkm.name;
console.log(name); // 'bulbasaur'
console.log(firstPkm.url); // Url
const fourthPkm = data.results[3];
console.log(fourthPkm.name); // 'charmander'
// Add a property from the API to our DOM
pkmParagraph.textContent = `${fourthPkm.name}`;
})
.catch((err) => console.log(err));
Using async await we can skip then
const pkmAPI = `https://pokeapi.co/api/v2/pokemon`;
async function displayPkm() {
const response = await fetch(pkmAPI); // Making a request
const data = await response.json(); // Handling the response
console.log(data.results[0].name); // Using data. Logging 'bulbasaur'
}
displayPkm().catch(err => console.log(err));
In order to understand APIs better and be able to manipulate different sections of a URL when using an API, we need to understand better how URLs work
A URL consists of ten parts: the scheme, subdomain, top-level domain, second-level domain, subdirectory, parameter, port, path, query and fragment, It does not always have all of them, but at least contains three of them
https://blog.hubspot.com/hubfs/URL%20Parts_300-02.jpg
https://
Tells web servers which protocol
to use when it accesses a page on your website
The most common protocol is HTTPS
.
https://
studio
Indicates which particular page of your website the web browser is serving up
https://studio.
youtube
The name of your website
https://studio.youtube
.com
Specifies what type of entity your organization registers as on the internet
.com
is intended for commercial entities in the United States, or .edu
intended for academic institutions
https://shop.yourstore.com/
hats
Refers to which particular section of a webpage you are on
Note: The subdomain is shop, and the subdirectory is "hats", that means this URL would serve up the "Hats" page, which is a subfolder of the "Shop" page. Where you should also be able to find Shirts, pants, etc
htttps://blog.hubstop.com:443/marketing
Specify a connection endpoint to direct data or specific service. It's associated with a host network address. The default port is 443
https://blog.hubspot.com/marketing
/parts-url
Specifies the location of the file or resource the user is accessing.
https://www.youtube.com/results
?
search_query=hello
A question mark that says a specific query is being performed and is used to procede a query string. A query string specifies the parameters of the data being asked from a website's database
Paremeters are the values being queried during a search. The parameter can be a number, encrypted value, search term or another data that can be found on the website. URL parameters contain a key
and a value
, separated by the equal sign =
A URL can contain multiple parameters, which are separated by ampersand &
Optional component of URLs, tipically placed at the end with a hash #
. Indicates a specific location on the webpage like an ID or name attribute
Cross Origin Resource Sharing
HTTP-header based mechanism that allows a server to indicate any origins (domaing, scheme or port) other than its own from which a browser should permit loading resourse.
For security reasons, browsers restrict cross-origin HTTP requests initiated from scripts. For example, fetch()
and XMLHttpRequest
follow the same-origin policy. This means that a web application using those APIs can only request resources from the same origin the application was loaded from unless the response from other origins includes the right CORS headers.
https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS/cors_principle.png
CORS can send an error when using fetch depending on the rules the server has, we can work with this by using a proxy
domainName.com - - - - - - somethingAPI.com // Can give an error
domainName.com - - - - Proxy - - - - somethingAPI.com
The API will check if the proxy is viable, then the proxy sends it to us
const pkmAPI = `https://pokeapi.co/api/v2/pokemon`;
const proxy = `https://cors-anywhere.herokuapp.com/`;
async function handleRecipe(query) {
const response = await fetch(`${proxy}${BASE}?search=${query}`);
const data = await response.json();
console.log(data);
}
Way to structure and organize your JavaScript and has the abilty to share functionality and data across multiple files or projects
To use modules we need to add the type module to our script tag inside our html
<body>
<script src="script.js" type="module"><script>
</body>
To use a functionality of a file inside of another file we need to export it first.
Two ways to export things, we can do it on the same line or at the bottom, which are called named exports
// File 1
export const something = "Kep1er"; // Exporting on the same line
const canadianTaxRate = "Not really...";
const howAreU = 'how are you?'
export function sayHi(name) { // Exporting on the same line
return `hi ${name}, ${howAreU}`;
}
function calculateTaxRate() {
const can = 42;
return can * 0.21;
}
// Exporting at the bottom of the file - Named exports
export { canadianTaxRate, calculateTaxRate };
To use it we need to import it inside the file we need it
Always do the imports on the top of the file
// File 2
import { calculateTaxRate, canadianTaxRate, sayHi } from "./taxRate.js";
const name = 'John Doe'
console.log(sayHi(name)); // Hi John Doe, how are you?
console.log(calculateTaxRate()); // 8.82
console.log(canadianTaxRate); // "Not really..."
The modules having their own scope means that using sayHi, we can even use it when it includes the variable howAreU
, which was declared on the other file
If we want to export a single value or to have a fallback value for your module, you could use a default export:
// module "my-module.js"
export default function cube(x) {
return x * x * x;
}
// Index js file
import cube from "./my-module.js";
console.log(cube(3)); // 27
// something.js
export const name = 'hi';
export const hello = 'hello';
export const isTrue = true;
export function sayHi() {
return 'hello'
}
// main.js
import * as everything from './something.js'
// Named as everything
console.log(name); // hi
If you only need to import something when you need it instead of when the script loads, you can use a different way
// currencies.js
const currencies = {
USD: 'United States Dollar',
AUD: 'Australian dolar',
}
export default currencies;
Import when using a button
// other-file.js
async function button(){
const currenciesModule = await import('./currencies.js')
}
Used to shorten our code. When we use an object or an array, to access their properties we had to do something like this
const person = {
name: "John",
age: 91,
}
// To access this values we would need to write the following:
const name = person.name;
const age = person.age;
We can simplify to this, by adding curly brackets on the left of the equal, we have to use the field names defined in the object
const {name, age } = {
name: "John";
age: 91,
} // name = John, age = 91
const user = ["John", "Doe"];
// Accessing the array index:
const firstName = user[0];
const lastName = user[1];
Instead we can move those variables to the left side of the equal using square brackets, the name can be whatever we can, the order is important. The first variable will take the value of the first index in the array
const [firstName, lastName] = ["John", "Doe"];
// firstName = John, lastName = Doe
Are a structured collection of data that allows efficient retrieval, insertion, updating and deletion of data.
There are two types of databases. Relation Databases (RDBMS) and NoSQL Databases. In this document we will mostly focus on SQL
, RDBMS
and PostgreSQL
Designed for specific data models and have flexible schemas. Examples include:
Key-Value Stores: Redis, DynamoDB
Column Stores: Cassandra, HBase
Graph Databases: Neo4j, Amazon Neptune
Document Stores: MongoDB, CouchDB
Stores data as collections of tables
. Relations are defined between tables for cross referencing.
The way it stores data makes users easy to understand the structure and content of the data. Developers may use Structured Query Language (SQL)
to query data, and add indexes to database for faster querying.
Examples of SQL Databases: MySQL, PostgreSQL, Oracle.
A relational database consists of a collection of tables (i.e. entities), from which we want to seek information. A table consists of columns, which are the properties of the table, and rows which are the records to store and retrieve.
Columns refer to a set of fields in tables. A column describes a property we are interested in storing for the table it belongs to.
A relationship is a connection between two entities. It connects data (in tables) together in meaningful ways. For instance, knowing the information of a transaction is meaningless without knowing the customer who performed the transaction so those tables are connected between
| EmployeeID | FirstName | LastName | Position | Department | Salary | HireDate | | ---------- | --------- | -------- | ----------------- | ---------- | ------ | ---------- | | 1 | John | Doe | Software Engineer | IT | 80000 | 2020-01-15 | | 2 | Jane | Smith | Data Analyst | IT | 75000 | 2019-06-01 | | 3 | Emily | Johnson | Product Manager | Marketing | 90000 | 2018-11-23 | | 4 | Michael | Brown | HR Specialist | HR | 60000 | 2021-03-10 | | 5 | Sarah | Davis | Financial Analyst | Finance | 70000 | 2017-08-14 |
Before going into SQL, some important concepts are:
Databases are used for several things, such as storing customer data, transaction records, inventory, storing user information, content, session data, etc!
They can be stored locally, or you can use a server.
When using servers they can be:
On-Prem/Company Server: Hosted internally and company full control of the server
Cloud Server/Serverless: Companies like AWS, Google Cloud and Azure. For a price they will manage many things
A query is a request for data from a database, allowing you to perform various operations:
| SalesID | DateKey | ProductKey | CustomerKey | StoreKey | Quantity | SalesAmount | | ------- | -------- | ---------- | ----------- | -------- | -------- | ----------- | | 1 | 20230101 | 101 | 1001 | 10 | 5 | 500.00 | | 2 | 20230101 | 102 | 1002 | 10 | 2 | 200.00 | | 3 | 20230102 | 101 | 1003 | 11 | 3 | 300.00 |
Describe attributes
or dimensions of the data, support filtering, grouping and labeling. Usually less rows of data but more descriptive| ProductKey | ProductName | Category | Price | | ---------- | ----------- | -------- | ------ | | 101 | Widget A | Widgets | 100.00 | | 102 | Widget B | Widgets | 50.00 | | 103 | Gizmo | Gizmos | 200.00 |
Structured Query Language is used for managing and manipulating relational databases. Used to perform various operations on the data stored in databases, such as querying, updating, inserting and deleting data. As well as managing database structures like tables, indexes and views
Databases we'll use to work with SQL:
Lightweight, file-based database
Ideal for small to medium applications
Zero configuration; easy setup
SQLite works on the web, in the link you can find the workspace where you can start creating tables and practicing as soon as you are there. Click the link above
Installation and use will be explained in detail later.
Advanced, open-source relational database
Suited for large applications
Supports complex queries, transactions
This are the keywords to work with SQL, from creating tables, to selecting elements and managing all your data.
Keywords are not case sensitive, so they can be lowercase if necessary, but using capital letters will make a better readability
(Just for testing, go into sqliteviz workspace and let's start practicing this keywords)
CREATE TABLE
- Defines a new table structure in a database. Also defines the columns and the data types
CREATE TABLE employee_table(
employee_id INTEGER,
name TEXT
);
-- Integer and Text are types of data
INSERT INTO
- Add new records (rows) into an existing table in the db, following the types
INSERT INTO employee_table VALUES
(1, 'John Doe'),
(2, 'Hebs'),
(3, 'Samuel Oak');
-- In PostgreSQL specify the columns in order too
INSERT INTO employee_tables (employee_id, name) VALUES
(1, 'John Doe'),
(2, 'Hebs'),
(3, 'Samuel Oak');
UPDATE
- Modifies existing data in a table
SELECT
- Identifies the columns (or data) from the database
FROM
- Identifies the table we are connecting to
*
- Special command to select all the columns
SELECT *
FROM table_example;
To select only a few columns, remove the *
and place your columns separated by commas
SELECT employee_id, name
FROM employee_table;
LIMIT
- query only a certain amount of rows
SELECT *
FROM employee_table LIMIT 5;
-- Will only show the first 5 rows
DISTINCT
- get unique rows. But is a resource intense
In this case it will show us the UNIQUE rows in the role of the employee list, if there is any duplicated won't be shown
SELECT DISTINCT employee_role
FROM employee_table;
WHERE
- sets a condition for the query. Filtiring it out
This example give sthe results with the name John
SELECT *
FROM employee_table
WHERE name = 'John';
ALTER TABLE
- Modifies an existent table. Can have multiple subcommands
-- Add a column
ALTER TABLE employee_table
ADD COLUMN new_column_hello TEXT;
-- Remove a column
ALTER TABLE exmployee_table
DROP COLUMN employee_id;
-- Rename a columnn
ALTER TABLE employee_table
RENAME COLUMN employee_id TO new_name_id;
-- Change type. (Serial is an auto increased number)
ALTER TABLE employee_table
ALTER COLUMN employee_id TYPE SERIAL;
-- Set default column
ALTER TABLE employee_table
ALTER COLUMN employee_id SET DEFAULT 0;
-- Drop default
ALTER COLUMN employee_id DROP DEFAULT;
UPDATE
- Used to modify existing rows in a table.
SET
- Specifies the column to be updated and the new value for that column
UPDATE table_name
SET column_name = new_value,
column_name2 = new_value2
WHERE condition;
Allow me a pause to give an example of this update keyword since it's gonna be used a lot, we should get familiar with it. Let's say we have this table and want to update some emails
| id | name | email | department | | --- | ------- | ------------------- | ---------- | | 1 | Alice | [email protected] | Sales | | 2 | Bob | [email protected] | Marketing | | 3 | Charlie | [email protected] | Sales |
We want to change Bob's email, he also joined Sales department for some reason! So we need to update the select wich the column and row of the data we need to change
UPDATE department_emails
SET email = '[email protected]',
department = 'Sales'
WHERE id = 2;
Should be changed like this:
| id | name | email | department | | --- | ------- | ---------------------- | ---------- | | 1 | Alice | [email protected] | Sales | | 2 | Bob | [email protected] | Sales | | 3 | Charlie | [email protected] | Sales |
--
Comments, used to document the query. /*Hi*/
is used for multiline
ORDER BY
- Specify a column and orders by the value, from low to hight. You can specify DESC
to make it descending order
SELECT *
FROM employee_table
ORDER BY employee_id DESC;
Null values
A field with no value, different from 0
or ""
-
WHERE salary_year_avg IS NULL;
DROP TABLE
- Deletes a table, be extremely careful since this can delete your whole database table ⚠️⚠️⚠️
DROP TABLE employee_table
SQL Keywords IMG - Credits to Sahn Lam and ByteByteGo for their great video on this topic: Roadmap for Learning SQL - YouTube
Data Definition Language (DDL):
Data Manipulation Language (DML):
Data Control Language (DCL):
GRANT
command.Transaction Control Language (TCL):
COMMIT
or ROLLBACK
statement.COMMIT
or ROLLBACK
statement.Constraints:
Other Important Keywords:
SELECT
statement.SELECT
, UPDATE
, or DELETE
statement.GROUP BY
clause.Understanding data types is crucial for designing and managing databases effectively.
Numeric Data Types
INT - Integer Values, commonly used for whole numbers
FLOAT / REAL - Floating-point numbers for approximate numeric values
DOUBLE - Double-precision floating numbers for more precision
DECIMAL / NUMERIC - Exact numeric values with specified precision
Character Data Types
CHAR(n) - Fixed lenght character string
VARCHAR(n) - Variable length character string with maximum of n
TEXT - Varialbe length character string with no specific limit
Date and Time Data Types
DATE - Date values (year, month, day)
TIME - Time values (hour, minute, second)
DATETIME / TIMESTAMP - Date and time values combined
INTERVAL - Represents a time span (6 days, 1 hour)
Boolean Data Types
TRUE
, FALSE
, NULL
Binary Data Types
Binary - Fixed-length binary data
VARBINARY - Variable-length binary data
Used within the WHERE
or HAVING
keywords. Used in conjuction with comparison operators:
=
, <>
, >
, <
, >=
and <=
AND
, OR
, BETWEEN
, and IN
For example to filter the data that's equal to a condition, we can use <>
, =
or NOT
-- NOT equal to 'Data Analyst'
SELECT *
FROM employee_table
WHERE job_role <> 'Data Analyst'
For numbers we can use the numeric ones, <
, >
, <=
, >=
-- Select from where salary is greater than 6000
SELECT job_location, job_id, salary_year_avg
FROM job_postings_fact
WHERE salary_year_avg > 6000
ORDER BY salary_year_
-- When salary is less or equal than 6000
WHERE salary_year_avg <= 6000
AND operator is used to get two conditions, you can have multiple AND. OR is for either one of the two conditions
-- More than 100000 while also being data analyst
WHERE salary_year_avg > 100000 AND job_title = 'Data Analyst'
-- More than 100000 or data analyst
WHERE salary_year_avg > 100000 OR job_title = 'Data Analyst'
-- Between two ranges
WHERE salary_year_avg BETWEEN 60000 AND 90000
-- In Specifies multiple values in a where clause
WHERE job_location IN ('Boston, MA', 'Anywhere')
To have more than one comparisions, we can wrap them in ()
.
In this next example whe want the people who lives in Boston, and two couples of comparisions, to do this we wrap everything in parenthesis to separate it
SELECT
job_location,
job_id,
job_title_short,
salary_year_avg
FROM job_postings_fact
WHERE job_location IN ('Boston, MA', 'Anywhere') AND
(
(salary_year_avg > 100000 AND job_title_short = 'Data Analyst') OR
(salary_year_avg > 70000 AND job_title_short = 'Business Analyst')
)
ORDER BY salary_year_avgr_avg
Used to substiute one or more characters in a string
They are used with the LIKE
operator
LIKE
, %
and _
are used inside WHERE
clause
WHERE job_title LIKE '%Data%'
%
- Represents zero, one or more characters. Used to query data that contains a specific word or character-- Query from all the job titles the ones that contain Analyst at any point
SELECT job_id, job_title
FROM job_postings_fact
WHERE job_title LIKE '%Analyst%'
-- Query for the ones that only have analyst as the first word
WHERE job_title LIKE 'Analyst%'
-- Only as last word
WHERE job_title LIKE '%Analyst'
_
- Represents one, single character. Used to query two or more words togetherWHERE job_title LIKE '%Expert Operations Data%'
AS renames a column in a table, a temporary name (alias).
Makes columns more readable
SELECT
job_title AS Job,
job_location AS Location,
job_via AS Online_platform,
salary_year_avg AS Salary
FROM job_postings_fact AS jpc
WHERE job_via LIKE '%Linkedin%'
Result of the query:
| Job | Location | Online_platform | Salary | | ------------------------------------------------------------------- | --------------------- | --------------- | ------ | | Data Center Engineer | Sydney NSW, Australia | via LinkedIn | 82500 | | Business Intelligence Analyst | Mountain View, CA | via LinkedIn | NULL | | Data Analyst | Las Vegas, NV | via LinkedIn | 55000 | | Data Analyst (Business) | Oakland, CA | via LinkedIn | 135000 | | Data Analytics Engineer (Transportation, Logistics and Fulfillment) | Sunnyvale, CA | via LinkedIn | 146500 | | Data Engineer | New York, NY | via LinkedIn | 120000 |
Are used to perform mathematical calculations
+
, -
, *
, /
, %
We can place this operations in different locations:
<u>SELECT Clause</u>
For performing calculations on data retreived from the database. Creates a temporary column that shows you the result of the operation
SELECT
salary,
salary * 0.1 AS Bonus,
salary + (salary * 0.1) AS Total
FROM salary_table
-- Bonus and Total are temporary, they are not added to the db
Example of result
| salary | Bonus | Total | | ------ | ----- |:----- | | 100 | 10 | 110 | | 2000 | 200 | 2200 |
<u> WHERE Clause</u>
For filtering data based on conditions that may involve arithmetic or logical operations
SELECT *
FROM orders
WHERE (quantity * unit_price) > 100;
<u>ORDER BY Clause</u>
For sorting the results based on expressions involving operations
SELECT
name,
age,
(current_date - birth_date) / 365 AS age_years
FROM people
ORDER BY (current_date - birth_date) / 365
<u>GROUP BY and HAVING Clauses</u>
For grouping rows that have the same values in specified columns and filtering groups with conditions involving operations
SELECT
department,
SUM(salary) AS total_salary
FROM employees
GROUP BY department HAVING SUM(salary) > 100000
After running this code, the departments will be grouped in their own section, HAVING will exclude the ones that are less than 100,000 when SUM adds all the salaries of the department. Example of this GROUP BY and HAVING:
Sample Table:
| department | employee_name | salary | | ---------- | ------------- | ------ | | IT | John | 60000 | | IT | Jane | 50000 | | HR | Alice | 70000 | | HR | Bob | 40000 | | Sales | Charlie | 30000 | | Sales | Dave | 20000 |
Process: Grouping by Department and filtering
IT: John (60000) + Jane (50000) = 110000
HR: Alice (70000) + Bob (40000) = 110000
Sales: Charlie (30000) + Dave (20000) = 50000
The HAVING
clause filters out the Sales department because its total salary (50000) is not greater than 100000.
Returns:
| department | total_salary | | ---------- | ------------ | | IT | 110000 | | HR | 110000 |
Modulo operator is used to get the reminder of dividing something. Also used to see if a number is even or odd
In this case, first we use WHERE
for the hours between 8 and 16, since more than 8 hours is considered extra hours.
SELECT
activity_id,
hours_spent,
hours_spent % 8 AS extra_hours
FROM invoices_fact
WHERE (hours_spent BETWEEN 8 AND 16) AND
extra_hours > 0
ORDER BY hours_spent
Refers to the process of summarizing or combining multiple rows of data into a single result based on a specified criterion. Aggregation functions are commonly used in conjuction with the GROUP BY
and HAVING
clauses
SUM()
- Adds together all the values in a specific column
COUNT()
- Counts the number of rows that match the specified criteria
AVG()
- Calculates the average value in a specific numeric column
MAX()
- Finds the maximum value in a set of values
MIN()
- Finds the minimum value in a set of values
Example Table:
| product | quantity | price | | ------- | -------- | ----- | | A | 10 | 5.00 | | B | 20 | 7.50 | | A | 15 | 5.00 | | B | 10 | 7.50 | | C | 5 | 10.00 |
SUM() deeper example
To find the total quantity sold for each product:
select product, SUM(quantity) AS total_quantity
FROM sales
GROUP BY product;
Result:
| product | total_quantity | | ------- | -------------- | | A | 25 | | B | 30 | | C | 5 |
HAVING
Filter query based on aggregate functions and groupings
Used because you can't aggregate functions in WHERE
Good practice: GROUP BY columns that are not aggregating}
SQL joins are used to combine ROWS from two or more tables based on a related column between them. The most common ones are: INNER JOIN
, LEFT JOIN
, RIGHT JOIN
, FULL JOIN
, CROSS JOIN
Syntaxis:
We will use dot notation to access a table and its columns
SELECT a.*, b.*
FROM table_a a
INNER JOIN table_b b
ON a.common_column = b.common_column;
For a better understanding on the topic, let's see our two tables first:
employees
| employee_id | name | department_id | salary | | ----------- | ------- | ------------- | ------ | | 1 | John | 1 | 60000 | | 2 | Jane | 2 | 50000 | | 3 | Alice | 1 | 70000 | | 4 | Bob | 2 | 40000 | | 5 | Diana | 3 | 30000 | | 6 | Charlie | 5 | 80000 |
departments
| id | department_name | | --- | --------------- | | 1 | IT | | 2 | HR | | 3 | Sales | | 4 | Marketing |
employees
is our Fact Table, departments
is our Dimension Table. They are connected with department_id
We are going to join both tables.
Returns only the rows that have matching values in both tables
-- We will show the name column of the employees table, and department_name
SELECT employees.name, departments.department_name
FROM employees
INNER JOIN departments
-- Specify what the connection is of each table
ON employees.department_id = departments.id;
Result of INNER JOIN:
| name | department_name | | ----- | --------------- | | John | IT | | Jane | HR | | Alice | IT | | Bob | HR | | Diana | Sales |
So what happened here?
INNER JOIN only returns the rows where there is a match in both tables, 'Charlie' is not included because there is no matching department_id
Returns ALL of the content of the column from table A, while from B it's only going to return the content that matches
SELECT employees.name, departments.department_name
FROM employees
LEFT JOIN departments
ON employees.department_id = departments.id;
Result of LEFT JOIN :
| name | department_name | | ------- | --------------- | | John | IT | | Jane | HR | | Alice | IT | | Bob | HR | | Diana | Sales | | Charlie | NULL |
LEFT JOIN
returns all rows from the left table (employees
) and the matched rows from the right table (departments
).
Since there is no match, the result NULL is added
We got all rows from our first table, and our second table only sent us what matches
The opposite to a LEFT JOIN, returns ALL the content of the column from table B, while A only will return what matches
SELECT employees.name, departments.departments_name
FROM employees
RIGHT JOIN departments
ON employees.department_id = departments.id;
Result of RIGHT JOIN:
| name | department_name | | ----- | --------------- | | John | IT | | Jane | HR | | Alice | IT | | Bob | HR | | Diana | Sales | | NULL | Marketing |
No one matches the Marketing
department, so we get all the rows from table B, and only the ones that match from A
SELECT employees.name, departments.departent_id
FROM employees
FULL JOIN departments
ON employees.department_id = departments.id
Result of FULL JOIN:
| name | department_name | | ------- | --------------- | | John | IT | | Jane | HR | | Alice | IT | | Bob | HR | | Diana | Sales | | Charlie | NULL | | NULL | Marketing |
This order is NOT the order you are placing the clauses, it's the order SQL process the information.
This sequence ensures SQL queries are processed efficiently and logically.
FROM/JOIN
WHERE
GROUP BY
HAVING
SELECT
DISTINCT
ORDER BY
LIMIT/OFFSET
An open-source relational database management system. Known for its robustness, extensibility and compliance with SQL standards.
It's used as the backend database for web applications, to handle user data, content and transactions, to store large volumes of data with complex queries and supports business applications that require hight reliability
Go to: https://www.postgresql.org and install the latest version for your operating system
On the installer, for most things you will keep the deafult, PostgreSQL will ask you to set a password, this is important so write it down somewhere. This password is used to define the superuser. If you change the port number, also write it down
Look for PgAdmin 4, this is the executable that will open PostgreSQL's admin dashboard where you can control it
VSCode it's our code editor where we will run our SQL queries. To connect our PostgreSQL to our VSCode:
Look for SQLTools in the Extensions section inside VScode
Install SQLTools PostgreSQL/Cockroack driver
We should see SQLTools on our left sidebar as a new icon with an option of "Add a new connection"
Select PostgreSQL, this is to connect to our PostgreSQL databases, name the connection, in Database place the name of the database you want to connect, in this case postgres
since it's the default, and below on Username also write postgres
since that's the default username.
Then Test connection
, enter the password you set for PostgreSQL Superuser and save
You will see your postgress connection in VS code, but we will not be changing this default database, create another one select the database to run queries on it.
Add a New SQL file on the icon above, you can test by typing:
CREATE DATABASE your_database;
And run your query, by selecting run or highlight your line and press and press Etrl + E, Ctrl + E (twice)
This will create a database that you will be able to see inside Pg Admin, it won't be activated until you click it there
Add another connection in VSCode, name your connection, type your database's name, write your username(postgres), test and save the connection.
To navigate between your connections you will see in the bottom left which database you are currently in.
Let's give an example of creating our first PostgreSQL database to add some notes too:
CREATE TABLE jobs_example(
job_id INT,
job_name VARCHAR(50),
date_join DATE,
resume_file TEXT,
has_experience BOOLEAN
row_index SERIAL
);
INSERT INTO jobs_example (job_id, job_name, date_join, resume_file, has_experience) VALUES
(1, 'Fullstack Dev', '2024-07-19', 'hebi_resume.pdf', true),
(2, 'Data Analyst', '2024-07-19', 'andy_resume.pdf', true),
(3, 'Finance analyst', '2024-07-19', 'john_resume.pdf', true),
(4, 'Data science', '2021-07-19', 'resume_final.pdf', true),
(5, 'Front end web dev', '2022-07-19', 'hello_resume.pdf', true),
(6, 'Database administrator', '2019-10-19', 'resume_final_FINAL_finaaaal.pdf', true);
SELECT *
FROM jobs_example
Semicolons are important, they mark that that's the end of the block wich allows you to write all of this and run it together since it's separated with semicolons. It will be taken as separated queries
The table should look like this:
| job_id | job_name | date_join | resume_file | has_experience | row_index | | ------ | ---------------------- | ---------- | ------------------------------- | -------------- | --------- | | 1 | Fullstack Dev | 2024-07-19 | hebi_resume.pdf | true | 1 | | 2 | Data Analyst | 2024-07-19 | andy_resume.pdf | true | 2 | | 3 | Finance analyst | 2024-07-19 | john_resume.pdf | true | 3 | | 4 | Data science | 2021-07-19 | resume_final.pdf | true | 4 | | 5 | Front end web dev | 2022-07-19 | hello_resume.pdf | true | 5 | | 6 | Database administrator | 2019-10-19 | resume_final_FINAL_finaaaal.pdf | true | 6 |
What happened here? If you played a bit with SQLite, you will note some differences. First, the INTEGER
is named INT
here, then INSERT INTO
needs to know which columns are going to receive data and then insert that data. This is important, if you leave something empty a NULL will take its position:
INSERT INTO jobs_example (job_id, job_name) VALUES
(1, 'dentist'),
(2, 'chef')
This will add two rows that will contain information in the columns job_id and job_name, but will give NULL to every other row (except row_index because SERIAL automatically fills a number)
| job_id | job_name | date_join | resume_file | has_experience | | ------ | -------- | --------- | ----------- | -------------- | | 1 | dentist | NULL | NULL | NULL | | 2 | chef | NULL | NULL | NULL |
You can export your table as JSON to access every property and its value
[
{
"job_id": 1,
"job_name": "Fullstack Dev",
"date_join": "2024-07-1",
"resume_file": "hebi_resume.pdf",
"has_experience": true,
"row_index": 1
},
{
"job_id": 2,
"job_name": "Data Analyst",
"date_join": "2024-07-19",
"resume_file": "andy_resume.pdf",
"has_experience": true,
"row_index": 2
},
]
SQL allows you to define constraints on columns and tables. Constraints give you as much control over the data in your tables as you wish. If a user attempts to store data in a column that would violate a constraint, an error is raised. This applies even if the value came from the default value definition.
Specify that the values in a certain column must satisfy a Boolean expression. For example to require positive product prices
CREATE TABLE procuts(
product_number INT,
name TEXT,
price NUMERIC CHECK(price > 0)
);
Specifies that a column must not assume the null value
CREATE TABLE products(
product_number INT NOT NULL,
name TEXT NOT NULL,
-- You can have more than one constrain
price numeric NOT NULL CHECK (price > 0)
);
Tip
In most database designs the majority of columns should be marked not null.
Ensures that the data contained in a column is unique among all the rows
CREATE TABLE products(
product_number INT UNIQUE,
name TEXT
);
-- Check for multiple columns
CREATE TABLE products(
product_number INT,
name TEXT,
something_else TEXT
UNIQUE (product_number, something_else)
);
Indicates that a column can be used as a unique identifier for rows in a table. The values must be both unique and not null.
Similar to when doing Joins, primary keys and foreign keys can be used to connect two or more tables. Basically it connects a column to another column
CREATE TABLE departments(
department_id INT PRIMARY KEY,
department_name TEXT NOT NULL
);
-- Primary keys can span more than one column
CREATE TABLE example(
a INT,
b INT,
c INT,
PRIMARY KEY (a, c)
);
Adding primary keys will automatically create a unique B-tree index on the column and will force to be marked NOT NULL
Specifies that the values in a column must match the values appearing in some row of another table
CREATE TABLE products(
product_number INT PRIMARY KEY,
name TEXT,
price NUMERIC
);
CREATE TABLE orders(
order_id INT PRIMARY KEY,
product_number INT REFERENCES products(product_number),
quantity INT
);
-- For multiple columns
CREATE TABLE orders1(
a INT PRIMARY KEY,
b INT,
c INT,
FOREIGN KEY(a, c) REFERENCES products(c1, c2)
)
Items can be removed, what if an item was removed while other table was referencing it? SQL allows to handle that. You could:
Disallow deleting the referenced product (RESTRICT
)
Delelte the referenced product as well (CASCADE
)
Set the referenced product to NULL or Update it (SET NULL
)
RESTRICT
prevents deletion of the referenced row, NO ACTION
it's the default raise an error since the referenced item does not exist.
CASCADE
specifies when the referenced row is deleted, the one that's referencing it should automatically be deleted too.
SET NULL
and SET DEFAULT
sets the row of the table who is referecing to NULL since the referenced does not exist. Instead of null you can also specify it to be the default value
CREATE TABLE tenants(
tenant_id INT PRIMARY KEY
);
CREATE TABLE users(
tenand_id INT REFERENCES tenants ON DELETE CASCADE,
user_id INT NOT NULL,
PRIMARY KEY (tenant_id, user_id)
);
CREATE TABLE posts(
tenant_id INT REFERENCES tenants ON DELETE CASCADE,
post_id INT NOT NULL,
author_id INT,
PRIMARY KEY (tenant_id, post_id),
FOREIGN KEY (tenant_id, author_id) REFERENCES users ON DELETE SET NULL (author_id)
)
Used to perform operations on date and time values
::DATE
- Converts to a date format by removing the time portion
TIMESTAMP / TIMESTAMP WITH TIME ZONE
- Data type, specifies the date and time values correctly, can be used with UTC
AT TIME ZONE
- Converts a timestamp to a specified time zone when selecting
EXTRACT
- Gets specific date parts (e.g. year, month, day)
First, let's explain ::
::
Is used to parse or "cast" a data type into another, for example, this strings are converted into the correct data type:
SELECT
'123'::INT,
'true'::BOOLEAN,
'2029-12-30':: DATE,
'3.14'::REAL;
As a result:
| int4 | bool | date | float4 | | ---- | ---- | ---------- | ------ | | 123 | true | 2029-12-30 | 3.14 |
You can use it with column names too, let's give a proper example:
CREATE TABLE events(
id SERIAL,
name VARCHAR(30) NOT NULL,
event_date TIMESTAMP
);
INSERT INTO events (name, event_date) VALUES
('Partyyy', '2029-10-12 14:30:00');
SELECT id,
name,
event_date::DATE
FROM events;
/*
We parse our event_date from TIMESTAMP to DATE, so we get only:
2029-10-12, without the hour
*/
Now how do TIMESTAMP WITH TIME ZONE
and AT TIME ZONE
work?
We first specify when creating the table the data type, it will be a Timestamp that includes time zone (UTC), also we don't want it to be null.
Then insert our data, this includes the date with the exact hour and the -06
is the UTC
CREATE TABLE events(
id SERIAL,
name VARCHAR(30) NOT NULL,
event_date TIMESTAMP WITH TIME ZONE NOT NULL
);
INSERT INTO events (name, event_date) VALUES
('partyyy', '2029-10-12 14:30:00-06'),
('Birthday!!', '2024-07-24 00:00:00-06');
SELECT id, name,
event_date AT TIME ZONE 'America/Mexico_City' AS mexico_time,
event_date AT TIME ZONE 'Asia/Tokyo' AS tokyo_time,
EXTRACT(MONTH FROM event_date) AS date_month
FROM events
ORDER BY id;
Result:
| id | name | mexico_time | tokyo_time | date_month | | --- | ---------- | ------------------- | ------------------- | ---------- | | 1 | partyyy | 2029-10-12 14:30:00 | 2029-10-13 05:30:00 | 10 | | 2 | Birthday!! | 2024-07-24 00:00:00 | 2024-07-24 15:00:00 | 7 |
We know that if we use SELECT
we retreive data from a table, what if we wanted to specify some data and move it into another? It's easier than it might sound like!
CREATE TABLE january_events AS
SELECT * FROM events
WHERE EXTRACT(MONTH FROM event_date) = 1
Let's imagine we have a big table with lots of events, similar to the table above we extract the month to compare for our WHERE condition, if it's one (because January is 1), get all the results where the month is january and create a table with those results
Is a way to apply conditional logic within your SQL entries
CASE
- Begins the expression
WHEN
- Specifies the condition to look at
THEN
- What to do if the condition is true
ELSE
- (optional), provides output if none of the condition is true
END
- Concludes the CASE
expression
Syntaxis
SELECT
CASE
WHEN column_name = 'Value1' THEN 'Description of Value1'
WHEN column_name = 'Value2' THEN 'Description of value2'
ELSE 'other'
END AS column_description
FROM table_name
Search CASE Expression
This will create another column named as you specify in the end of your case, that will change the data to the one in the cases
For example:
SELECT
job_title,
CASE job_title
WHEN 'Data Analyst' THEN 'Analysisssss'
WHEN 'Data Scientist' THEN 'Science big brain'
ELSE 'Unknown'
END AS department_name
FROM february_jobs
Would give something like:
| job_title | department_name | | ------------------------- | ----------------- | | Senior Data Engineer | Unknown | | Data Analyst | Analysisssss | | Data Analyst | Analysisssss | | Data Scientist | Science big brain | | Data Scientist | Science big brain | | Data Scientist | Science big brain | | Senior Data Engineer | Unknown | | Software Engineer | Unknown | | Data Scientist | Science big brain | | Machine Learning Engineer | Unknown |
You can then aggregate and count how many are there
This can also be used to clarify the status of something:
SELECT order_id,
customer_id,
order_date,
status,
CASE
WHEN status = 'P' THEN 'Pending'
WHEN status = 'C' THEN 'Completed'
WHEN status = 'S' THEN 'Shipped'
WHEN status = 'R' THEN 'Returned'
ELSE 'Unknown'
END AS status_description
FROM orders;
Subqueries/Nested queries and Common Table Expressions(CTEs) are used for organizing and simplifying complex queries by using the result of one query as input to another.
They are used when you want to perform a calculation before the main query can complete its calculation, they are useful in several scenarios for example filtering on aggregated values or calculations
Subquerie example
SELECT name, salary
FROM employees
WHERE salary = (
SELECT MAX(salary)
FROM employees
WHERE department_id = 1
);
CTEs
Common Table Expressions are a way to write complex queries breaking them down into simpler, reusable components. It's defined using WITH
It's a Temporary result that you can reference, only exists during the execution of a query.
WITH numbered_rows AS(
SELECT job_id
FROM jobs
ORDER BY job_id
)
Delete from jobs
Where job_id > 0;
Union combines the results set of two or more SELECT statements (only distinct values)
SELECT city FROM customers
UNION
SELECT city FROM suppliers
ORDER BY city;
The UNION ALL
command combines the result set of two or more SELECT statements (allows duplicate values).
SELECT city FROM customers
UNION ALL
SELECT city FROM suppliers
ORDER BY city;
;