Javascript

JS, from nothing to something

Variables

var, let, const. var isn't used any more.

let myVariableName = "Hi";
myVariableName = "let variables can be reasigned";

// const variables don't change values after assignment
const myOtherVariable = "const variables con NOT be reassigned";

// Uppercase constants
// Widespread practice to make them uppercase when constants
// are 'hard-coded', calculated constant should be lowercase
const POST_ENDPOINT = "https://myapi/com/post";
const COMMENTS_ENDPOINT = "https://myapi/com/comments";
const COLOR_BLUE = "#00f";
const myAge = calculateAge('01/01/1991');

Data Types

number:

let age = 1;

bigint:

let bigNumber = 12345n;

string:

let userName = "User Name";

boolean:

let userIsCool = true; // `false` is the other value possible

null:

let nothingHere = null; // means nothing, empty

undefined:

let notAssigned; 
// console.log(notAssigned) prints `undefined`
// means `value not assigned`

object:

let userIndo: {
  name: "User Name",
  lastName: "User Last Name"
}

symbol

Rarely used.

Note: You can print what type of Data type variable is with typeof myVariable // "string" or typeof(myVariable)


Interaction

  • alert: alert("Hi") simple message to user in a modal way
  • prompt: let age = prompt("How old are you", 1) a modal with question, second parameter, in this case number 1, is optional and it will autofill input with that value, let age will store data user wrote down.
  • confirm: let isOk = confirm("Is it okay to show you this modal") modal with Ok and cancel buttons, isOk will store true if Ok is pressed or false otherwise.

Type Conversions

To String String()

let value = true;
value = String(value); // "true", if null = "null" etc

To Number Number()

let value = "123";
value = Number(value) // 123;

Number("123z"); // NaN, undefined = NaN
Number(true); // 1, false or null = 0

To Boolean Boolean()

Boolean(1); // true
Boolean(0); // false

Boolean(""); // false
Boolean("0"); // true, "string not empty" = true

Operators

  • Addition +
  • Subtraction -
  • Multiplication *
  • Division /
  • Remainder %
  • Exponentiation **

Remainder %

The result of a % b is the remainder of the division a by b

5 % 2; // 1
8 % 3; // 2

Exponentiation **

2 ** 2; // 2² = 4
2 ** 3; // 2³ = 8
2 ** 4; // 2⁴ = 16

String concatenation with +

"my" + "string"; // "mystring"
"1" + 2; // "12"
2 + "1"; // "21"

// this is because it works from left to right, it does the math
// with two numbs like 2 + 2 and later adds the 1 string
2 + 2 + "1"; // "41" and not "221"

// The 2 gets concatenated to '1', so it’s like '1' + 2 = "12" 
// and "12" + 2 = "122".
"1" + 2 + 2); // "122" and not "14"

/*
* so, sum `+` is the only one that works that way
* other arithmetic operators work only with numbers 
* and always convert their operands to numbers.
*/
6 - "2"; // 4, converts '2' to a number
"6" / "2"; // 3, converts both operands to numbers

Chaining assignmets

let a, b, c;
a = b = c = 2 + 2; // a = 4, b = 4, c = 4

Modify in place

let n = 2;
n = n + 5; // n = 7

n += 2; // n = 9
n *= 2 + 2; // n = 36 (9*4)

Increment.decrement

let n = 2;
n++; // n = 3

n--; // n = 2 (suppusing it was 3 before)

you can plase operators ++ and -- where you want, before of after variable, but there are diference in returning values since JS works from left to right.

let n = 1;
let m = ++n; // m = 2 (it increments first n and return new n value)

let n = 1;
let m = n++; // m = 1 (it returns n first (1) to m, and increments later, so (n = 2))

let n = 1;
let m = 2 * ++n; // m = 4

let n = 1;
let m = 2 * n++; // m = 2

Comparison

Greater/lest than a > b, a < b

Greater/less than or equals a >= b, a <= b

Equals/not equals a === b, a !== b

Result of a comparison is always a boolean, true or false


Functions

Function Declaration

function helloWorld (param) {}

helloWorld("My Name");

Function Expression

// The name of the function after = is optional
const myFunction = function myOptionalName(param) {}

myFunction("Parameter");

// Anonymous function
const myOtherFunc = function() {}
const arrowFunc = (param) => {}

arrowFunc("Param");

IIFE (Immediately Invoked Function Expression)

(function () {
	let num = 1;
	return num;
 })()
 // 1

Objects

Objects Properties

let customPropertie = 'customObjectProperieName';

let person = {
	name: 'Jonny',
	age: 28,
  	partTime: false,
  	[customPropertie]: 'Custom Value'
}
person.parTime = true;
person['age'] = 0;

person.name; // Jonny
person.parTime; // true
person['age']; // 0
person['customObjectProperieName']; // Custom Value

Objects Methods Attaching functions to objects

let person = {
  name: "Jonny",
  showName: function (age) {
    console.log(`${this.name} is ${age}`);
  },
};

person.showName(28); // Jonny is 28

Passing Objects to Functions A function can edit an object property since we are passing the reference of the object

let person = {
  age: 27,
};

const incrementAge = (person) => {
  person.age++;
};

incrementAge(person);

person.age; // 28

Built-in Objects JS has many built-in object that developers use every day, to modify string, to do maths or to get dates, etc. so, you can check the list here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects

Document Object Model (DOM) You can check more about this object on: https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model

// Get element by ID and change text content
document.getElementById("message").textContent = "New Message";

// Style it
const header = document.getElementById("header");
header.style.color = "#FFF"; // change color to white
header.style.fontWeight = "800"; // font weight bold

// Listener, click
const button = document.getElementById("read-more");

button.addEventListener("click", function () {
  const banner = document.getElementById("banner");
  // Check if element has class
  if (banner.classList.contains("d-none")) {
    // Remove class from element
    banner.classList.remove("d-none");
  } else {
    // Add class to element
    banner.classList.add("d-none");
  }
});

Arrays

Create and initializing arrays

// Create an Array
let values = [];

// Create an Initializing
let moreValues = [1, 2, 3];

// Array.of
let otherValues = Array.of('a', 'b', 'c'); // ['a', 'b', 'c']

// An Array is an object in js
typeof otherValues; // object

// Check if variable is an array
Array.isArray(values); // true

one can find more about arrays in https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array

Accessing Array Items

let values = ['a', 'b', 'c'];
values[0]; // a
values[1]; //.b
values[2]; // c
values[3]; // undefined

// Edit value
values[0] = 'aaa';
values[0]; // aaa

Manipulating Arrays

// push(), Add item at the end
let values = ['a', 'b', 'c'];
values.push('d'); // you can push more than one value, for example values.push('d', 'e', 'f');
values; // a,b,c,d

// pop(), get latest but removes it from array
const last = values.pop();
last; // d
values; // a, b, c

// shift(), get first and removes it from array
const first = values.shift();
first; // a
values; // b, c

// unshift(), add Item to the beginning of the array
values.unshift('a'); // you can add more thant one, values.unshift('hello', 'world');
values; // a, b, c

// slice(), Creates a new array from another
const values2 = ['a', 'b', 'c'];
const newValues = values2.slice(1, 2); // first argument, where we want to start ('b') and second argument, were we want to finish ('c');
newValues; // b

// slice() without arguments
const newValues2 = values2.slide();
newValues2; // a, b, c


// splice(), delete items from array
values2.splice(1, 1); // first argument, index where we start, second argument, number of items we want to delete
values2; // a, c

// splice to insert item
values2.splice(1, 0, 'foo'); // starts in index 1, deletes 0 elements, inserts foo
values2; // since values 2 was [a,c], then, values2 = a, foo, c

Array Searching and Looping

// indexOf, search index of item in array
const values = ['a', 'b', 'c'];
values.indexOf('c'); // 2
values.indexOf('d'); // -1, when one element is not in array, we don't get undefined, we get -1 instead;

// filter(), create an array with filter condition
const newValues = ['a', 'bbb', 'c'];
const found = newValues.find(item => item.length > 1);
found; // bbb

// forEach, you can also use a for or a while to iterate over a loop
values.forEach((item) => {
  console.log(item); // a, - b, - c
});

Scope & Hoisting

Global Scope A variable, function etc writted outside a function becomes global and you can access its value inside any function after it.

let id = 12345;

function showId() {
	console.log(productId); // 12345
}

showId();

Function Scope A variable inside a function, is only accessible inside it

function showId() {
  let id = 12345;
 
  function inside() {
  	let id = 67890;
    console.log(id); // 67890; however if we delete `let id = 67890;` above 
    // then this id will print 12345 since it will look for variable in the upper scope
  }
  inside();
  
  console.log(id); // 12345
}

showId();

console.log(id); // ERROR, ReferenceError: id is not defined

Hoisting You can NOT access a variable before is declared using let or const, you can with var but since var is not used anymore, I'm omitting anything related to that.

console.log(myConstant); // ERROR: ReferenceError: myConstant is not defined
const myConstant = 12345;

HOWEVER, you CAN access a function before is declared, this is called Hoisting

Hoisting: Hoisting is JavaScript's default behavior of moving all declarations to the top of the current scope (to the top of the current script or the current function).

myFunction();

function myFunction() {
  console.log(12345); // 12345
}

so, in this case function myFunction() declaration will be moved at the top, before myFunction() call.


All about the switch

Switch is recommended when more than two if conditional are required.

switch(<expression>) {
       case <expression 1>:
       // statement(s)
       break; // exist out of switch

       case <expression 2>:
       case <expression 3>:
       case <expression 4>:
       // statement(s)
       break;

       default: // if no other case is matched
       // statement(s)
       break;
}

Example

const productId = 2;

switch (productId) {
  case 1:
    console.log("Product 1");
    break;
  case 2:
    console.log("Product 2"); // this is printed
    break;

  default:
    console.log("Unknown product");
    break;
}

Multiple Case Statements

const color = "Blue";

switch (color) {
  case "White":
    console.log("Color white");
    break;
  case "Red":
  case "Blue":
    console.log("Color Blue or Red"); // this is printed
    break;

  default:
    console.log("Unknown Color");
    break;
}

Note:

  • Don't forget break, if you forget break, it will execute case 1, case 2, case 3 etc until there is a break.
  • Switch use stric mode to check condition, if your condition is "2" as string, and your cases are case 2 as number, it wouldn't work

Block-level Scope In a switch, the swtich itself is a block, but the case aren't, let's see an example:

const productId = 2;

switch (productId) {
  case 1:
   	let message = "My First Message";
    console.log(message);
    break;
  case 2:
    let message = "My second Message";
    console.log(message);
    break;

  default:
    console.log("Unknown product");
    break;
}

this will break and it will say message is already declared because cases aren't blocks, to make them block, you can use {} in cases:

const productId = 2;

switch (productId) {
  case 1: {
   	let message = "My First Message";
    console.log(message);
    break;
  }
  case 2: {
    let message = "My second Message";
    ...

The difference Between for/in and for/of

for/in

  • Iterates over elements of an object(properties and methods)
  • Returns key (property/method) name
  • object[key] returns value
let user = {
	"name": "Jonny",
  "age": 28,
  "calculateAgeIn100Years": function(){
  	return this.age * 100;
  }
}

//for in loop
for (const key in user) {
  console.log(`\'${key}\'= ${user[key]}`);
}

// Result console log
/*
'name'= Jonny
'age'= 28
'calculateAgeIn100Years'= function () {
    return this.age * 100;
  }
*/

for/of

  • Iterates over values in array, string, etc
  • Returns object for each iterarion
// Array
let array = [
  "string item",
  12345,
  {
    property1: "String",
    property1: 123,
  },
];

// for of
for (const item of array) {
  console.log(item);
}

// Result
/*
new one
12345
{ property1: 123 }
*/
// String
let string = "my text";

// for of
for (const char of string) {
  console.log(char);
}

// Result
/*
m
y
 
t
e
x
t
*/

Break and Continue Break: Leave a loop early

Continue: Next iteration of a loop

// Break
const products = [
  {
    price: 20,
  },
  {
    price: 30,
  },
  {
    price: 40,
  },
];

for (const product of products) {
  if (product.price >= 30) {
    break;
  }

  console.log(product);
}

/*
{ price: 20 } // only print this since loop breaked
*/

// Continue
for (const product of products) {
  if (product.price === 30) {
    continue;
  }

  console.log(product);
}

/*
// Doesn't print 30 since conditional says that when equal 30 continue and don't
// do the rest of things in loop
{ price: 20 }
{ price: 40 }
*/

Note: Labeled statements aren't recommended to use anymore to avoid spaghetti code, so, it is not mentioned here


Working with logical operators and short-circuit evaluation

Truthy and Falsy

  • Truthy: Any variable with a value ("String", 10, true) is true
    • empty arrays [] or objects {} are truthy too, in that case we have to check in other way if they are empty or not
  • Falsy: Any variable (false, null, undefined, Nan, 0, '') is false

Logical Operators

let price = 20;

// AND (&&) operator
if (price > 10 && price < 30) {
  console.log("true");
}

// OR (||) operator
if (price < 10 || price < 30) {
  // First conditional is not true BUT
  // second one is and the `or` will enter here
  console.log("true");
}

// NOT(!) operator
if( !(price < 10)){
  // it entes here, since price is NOT less than 10
  console.log("true");
}

Short Circuiting

const isColorRed = (color) => color === "Red";
const isGreaterThan100 = (num) => num > 100;

let result;

// if first is false, the second part is never evaluated
result = isColorRed("Black") && isGreaterThan100(101);
console.log(result); // false

// Each expression is evaluated until one returns true
// the rest are then skipped
result = isColorRed("Red") || isGreaterThan100(101);
console.log(result);

Exception Handling

Always add try...catch around 'dangerous' code

try {
  // Some code that could fail
} catch (error) {
  // Do something with the error
} finally {
  // This code always runs
}

Example:

function simpleTryCatch() {
  let result;

  try {
    console.log("Before error");
    result = x / 10;
    // after an error, anything else is not run/executed
    console.log("This line will not be printed");
  } catch (error) {
    console.log(error.message);
  } finally {
    // this always run, not matter if it is successfully or on error
    console.log("In the final block");
  }
}

simpleTryCatch();
//Before Error
// x is not defined
// In the final block

Throw

  • Can throw your own custom error
  • Create an object with at least two properties: "message" and "name"
function attemptDivision() {
  let result;

  try {
    result = x / 10;
  } catch (error) {
    throw {
      message: `In the attemptDivision() method the following error occurred: ${error.message}`,
      name: "Custom error",
    };
  }
}

attemptDivision(); // Custom error: In the attemptDivision() method the following error occurred: x is not defined

Detect the Error Type

function checkErrorType() {
  let result = 0;

  try {
    // Reference error because 'x' is not defined
    result = x / 10;
    // Range error because a number cannot have 200 significant digits
    result.toPrecision(200);
    // Type error because result is a numeric
    result.toUpperCase();
    // URI error
    let uri = "https://google.com/path%%%/file name";
    decodeURI(uri);
    // Syntax error because missing a final single quote
    let sum = eval("alert('Hello)");
  } catch (error) {
    handleError(error);
  }
}

function handleError(error) {
  switch (error.name) {
    case "ReferenceError":
      console.log("Reference error: " + error.message);
      break;
    case "RangeError":
      console.log("Range error: " + error.message);
      break;
    case "TypeError":
      console.log("Type error: " + error.message);
      break;
    case "URIError":
      console.log("URI error: " + error.message);
      break;
    case "SyntaxError":
      console.log("Syntax error: " + error.message);
      break;
    case "EvalError":
      console.log("Evaluation error: " + error.message);
      break;
    default:
      console.log("Error Type: " + error.name + " - Message: " + error.message);
      break;
  }
}

checkErrorType();

How to determine variable data types

Primitive Data Types Bolean: true or false null: no value undefined:a varialbe declared, but has no value number: integers, decimals, float, etc string: a series (array) of characters

Evrything in JS inherits from object data type except the primitives above

Object Data Types new Array: A collection of values new Error: Contains a name and an error message new Function: A block of code new Object: A wrapper around any type new RegExp: A regular expression

Use primitives boolean, number or string instead of these when possible since these take more memory space and are slower to access new Boolean: An object that contains true or false new Number: An object that contains numberic value new String: An object that contains a character(s)

typeof/constructor/instanceof To get to know what kind of variable is, we use typeof <variable>

const hi = "my string";
const newDate = new Date();

console.log(typeof hi); // string
console.log(typeof newDate); // object

// Constructor
console.log(hi.constructor.toString()); // function String() { [native code] }
console.log(newDate.constructor.toString()); // function Date() { [native code] }

// instanceof
// this only check for object data types
console.log(hi instanceof String); // false, since strin was declared as primitive
// data type, if we declared it like new String("Hi") it will fire true
console.log(newDate instanceof Date);// true

Understanding 'this' in Javascript

'this' keyword is often used in many object-oriented propgraming languages

  • Refers to an object
  • That object is typcally in which the current code is running
  • Sometimes the object can be changed

Diferent value based on execution context:

  • In a method: owner object
  • In a function: global object
  • In an event: element that received that event
  • call()/apply methos refet to object passed in

Global window object:

// Javascript runs within the global window object availiable in every browser
<script>
console.log(this.toString()); // [object Window], this = global window object
console.log((this === window).toString()); // true

// that is not a good convention, and using 'use strict' it will make this undefined
// better use `window` eg: (window.toString()) for example to get global window object

</script>

Object literal:

const person = {
  name: "Jonny",
  fullName: function () {
    console.log(this.name); // Jonny, this = person object literal
  },
};

person.fullName();

Constructor function:

function Person(first, last) {
  this.firstName = first;
  this.lastName = last;

  this.fullName = function () {
    console.log(this.firstName); // this = current Person Object
    console.log(this.lastName);
  };
}

const person1 = new Person("Jonny", "Acevedo");
person1.fullName(); // Jonny, Acevedo

'this' in Event Handlers 'this' in the context of this event refers to the html element that it's attached to.

<button onclick="eventHandler(this)">
  Pass this to function handler
</button>

<script>
  function eventHandler(ctl){
	console.log(ctl.toString()); // [object HTMLButtonElement]
	}
</script>

Spread Operator

  • Expand any 'iterable' sich as a string or array into and array
  • For passing multiple arguments to method
  • The syntax uses the ellipsis symobl ...
  • Always o nthe right-side of an equal sign

String to array

const productSKU = "ABC-12345-67";
const values = [...productSKU];

console.log(values); // [ 'A', 'B', 'C', '-', '1', '2', '3', '4', '5', '-', '6', '7' ]

Copy Array

const arr = [1, 2, 3];
const arr2 = [...arr];

arr2.push(4);
arr2[0] = 90;

console.log(arr); // [ 1, 2, 3 ]
console.log(arr2); // [ 90, 2, 3, 4 ]

Copying and array with object inside it, the spread operator will NOT copy the objects on it by value, it will only copy by reference, so, if you change an object property in the new array IT WILL CHANGE in the old one

const product = [
  {
    price: 1234,
  },
];

const newProduct = [...product];

newProduct[0].price = 1000;

console.log(product[0].price); // 1000
console.log(newProduct[0].price); // 1000

Concatenate two arrays

const IDs1 = [1, 2, 3, 4];
const IDs2 = [5, 6, 7, 8];

const allIds = [...IDs1, ...IDs2]; // [ 1, 2, 3, 4, 5, 6, 7, 8 ]

Using Spread to pass parameters to a constructor or to a function

// constructor
const dt = new Date(2021, 07, 21); // 2021-08-21T

const dataFields = [2021, 07, 21];
const newDt = new Date(...dataFields); // 2021-08-21T

// function
const myFunction = (arg1, arg2, arg3) => {
  console.log(arg1); // 2021
  console.log(arg2); // 08
  console.log(arg3); // 21
};

myFunction(...dataFields);

Shallow copy on object literals it is very similar to the the javascript object.assign method, so it does copy property by property from one object to the other

const product = {
  price: 1245,
};
const newProduct = { ...product };
newProduct.price = 1000;

console.log(product); // { price: 1245 }
console.log(newProduct); // { price: 1000 }

Using Variables Literals and Assignments

const name = "User's Name";

// ONE LINE TEMPLATE LITERAL
const helloText = `My Hello text ${name} `; // My Hello text User's Name

// MULTIPLE LINES
const multipleLines = `My First Line ${name}
${helloText}
Third Line
`;
// My First Line User's Name
// My Hello text User's Name
// Third Line

Create a tagged Template Literal Adding bold to the values tags inside a template literal:

const name = "User's Name";

const helloText = myTaggedTemplateLiteral`My Hello text ${name}`;
// My Hello text <b>User's Name</b> 

function myTaggedTemplateLiteral(strings, ...values) {
  let str = "";
  for (let i = 0; i < strings.length; i++) {
    if (i > 0) {
      str += `<b>${values[i - 1]}</b>`;
    }

    str += strings[i];
  }
  return str;
}

String.raw So, template literals don't render backslashes\ for example, so, to do so, you can use String.raw

const newTemplate = `text \ text`; // text  text

//String.raw
const anotherTemplate = String.raw`text \ text`; // text \ text

Destructing Arrays and Objects

This allows to unpack and get values from arrays and objects very simple

Array destructuring

const numbers = [1, , 3, 4, 5, 6];
const [number1, number2 = 2, number3, ...moreNumbers] = numbers;

console.log(number1); // 1
console.log(number2); // 2  -> Here, in the array position, it's empty, so, with `number2 = 2` we defined an default value of 2
console.log(number3); // 3
console.log(moreNumbers); // [ 4, 5, 6 ]

Object destructuring

const me = {
  name: "Jonny",
  lastName: "Acevedo",
  skills: ["JS", "React", "Flutter"],
  age: 28,
  city: "Medellin",
};

const {
  name,
  lastName,
  skills,
  undefinedProperty = "Default Value",
  ...moreProperties
} = me;

console.log(name); // Jonny
console.log(lastName); // Acevedo
console.log(skills); // [ 'JS', 'React', 'Flutter' ]
console.log(undefinedProperty); // Default Value
console.log(moreProperties); // { age: 28, city: 'Medellin' }