Skip to content

Latest commit

 

History

History
2974 lines (2179 loc) · 81.2 KB

javascript-basics-for-beginners.md

File metadata and controls

2974 lines (2179 loc) · 81.2 KB

Getting Started

What is JavaScript

Frequently Ask Questions about JavaScript

  1. What is JavaScript? JavaScript is one of the most popular and widely used programming languages in the world right now.
    72,000/year is the average Salary of the JavaScript Developer in the US.
    You can work as a Front-end Developer, Back-end Developer or a Full-stack Developer.

  2. What can you do with it?

    • Web / Mobile Apps
    • Real-time Networking Apps
    • Command-line Tools
    • Games
  3. Where does JavaScript code run? JavaScript was originally designed to run only in browsers, so every browsers has JavaScript Engine that can execute JavaScript code.

    • FireFox: SpiderMonkey
    • Chrome: v8

In 2009 Ryan Dahl took the open-source JavaScript Engine in Chrome and embedded it inside a C++ program, became Node.
Now with this we can run JavaScript code outside of the browser, so we can pass our JavaScript code to node for execution, and this means with JavaScript we can build Back-end for our Web and Mobile Application.
Browsers and Node provide a run-time environment for our JavaScript code.

  1. JavaScript vs ECMAScript?
    • ECMAScript is just a specification
    • Javascript is a programming language that confirms to this specification.

ECMA is a organization responsible for defining standards. They take care of the ECMAScript specification.

Setting Up the Development

  1. Download Editor
    • Visual Studio Code (VSCode)
    • Sublime Text
    • Atom
  2. Download Node

JavaScript in Browsers

There are 2 places where we can add a script element.

  1. head section
  2. body section

Best practice is to put script element at the end of the body section after all the elements.
2 reasons for that:

  1. The browsers parses the file from top to bottom, so if you put the script element at the head section, then the browser will get busy parsing and executing the javaScript code and it won't be able to render the content of the page. This will create a bad user experience. Your user looks inside your web page, it's white or blank while your browser is busy pasinging and executing your javaScript code.

  2. Almost always the code in between script element needs to talk to the elements on the wb page.
    Ex. You may want to show or hide some elements. By adding the code at the end of the body section will be confident that all the element are rendered by the browser.

There are exception to this rule, sometimes you're using third party code that has to be place in the head section.

Statement is a piece of code that expresses an action to be carried out. In this case we want to log a message on the console.
All statements in the javaScript should be terminated by a semi-colon.

console.log("Hello World");

The content inside the quotes are string.
A String is a sequence of characters.

Comment is not executed by javaScript, and it is intended for documenting your code.

// This is a comment

Basics

Variables

We use a variable to store data temporarily in a computer's memory

let name = "Genesis";
console.log(name);

/*
Variables:
Cannot be a reserved keyword
Should be meaningful
Cannot start with a number (1name)
Cannot contain a space or hyphen (-)
Are case-sensitive
*/

let firstName = "Genesis",
  lastName = "Gabiola";

// Modern best practice is to define each variable in a single line

let firstName = "Genesis";
let lastName = "Gabiola";

Constant

Best practice: If you don't need to reassign, const should be the default choice, otherwise if you need to reassign a variable use let

const interestRate = 0.3;
interestRate = 1;
console.log(interestRate);

Primitives

Two Categories of Types:

  1. Primitives / Value Types
    • String
    • Number
    • Boolean (true or false)
    • undefined (if you don't initialize the variable by default it's undefined, but it can explicitly set it to undefined)
    • null If you want to clear the value of the variable
    • symbol (es6)
  2. Reference Types
let name = "Genesis"; // String Literal
let age = 25; // Number Literal
let isApproved = true; // Boolean Literal
let firstName = undefined;
let selectedColor = null;

Dynamic Typing

Two Types of Programming Languages

  1. Static (Statically-typed)

    • when declared a variable, the type of that variable is set and it cannot be change in the future
    string name = 'John';
    
  2. Dynamic (Dynamically-typed)

    • the type of the variable can change
    let name = "John";

Objects

Reference Types:

  • Object
  • Array
  • Function

An Object in javaScript and in other programming languages is like an object in real life.
Ex. A person has name, age, address and so on... these are the properties of a person.

let person = {
  // Object Literal
  name: "Genesis",
  age: 25
};

console.log(person);

Properties of the person object:

key value
name 'Genesis'
age 25

Two ways to work with the properties:

  1. Dot Notation is a bit more concise and shorter and should be the default choice
person.name = "John";
  1. Bracket Notation [ ] has it's own uses, sometimes you don't know the name of the target property until the run time.
person["name"] = "Mary";

Arrays

Array is a data structure to present a list of items.

let selectedColors = ["red", "blue"]; // Array Literal
selectedColors[2] = 1;
console.log(selectedColors);

Functions

Functions are one of the fundamental building blocks in javaScript, a function is basically a set of statement that performs a task or calculates and return a value.

function greet(name, lastName) {
  // declaring the function with parameter name
  console.log("Hello World" + name + " " + lastName);
}

greet("John", "Smith"); // Calling the function with argument

// Output: Hello John Smith

Parameter is a like variable inside the parenthesis and only meaningful inside the function.

Argument is the actual value that will supply the parameter.

Types of Functions

// Performing a task
function greet(name, lastName) {
  console.log("Hello World" + name + " " + lastName);
}
// Calculating a value
function square(number) {
  return number * number;
}

console.log(square(2)); // 4

Operators

In JavaScript, we have different kinds of operators, we use this operators along with our Variables and Constants to create Expressions, and with this expressions we can implement logic and Algorithms.

Operators in JavaScript:

  • Arithmetic
  • Assignment
  • Comparison
  • Logical
  • Bitwise

Arithmetic Operators

We use this for performing calculations just like the calculations we have in mathematics.

Expression is something that produces a value.

let x = 10;
let y = 3;

// In this example, we have the expression inside the console
console.log(x + y); // Addition
console.log(x - y); // Subtraction
console.log(x * y); // Multiplication
console.log(x / y); // Division
console.log(x % y); // Remainder
console.log(x ** y); // Exponentiation
// Increment
console.log(++x); // 11
console.log(x++); // 10 then 11
// Decrement
console.log(--x); // 9
console.log(x--); // 10 then 9

Assignment Operators

Assigning a value to x.

let x = 10;

x++; // x = x + 1;
x += 5; // x = x + 5;
x *= 3; // x = x * 3;

Comparison Operators

We use this operator to compare the value of a variable to something else.

let x = 1;

// Relational Operators
console.log(x > 0); // true
console.log(x >= 1); // true
console.log(x < 1); // false
console.log(x <= 1); // true

// Equality Operators
console.log(x === 1); // true
console.log(x /== 1); // false

Equality Operators

  1. Strict Equality Operator ensures that both have the same Type and same Value.

  2. Loose Equality Operator doesn't care about the types matching. If the types don't match it will convert it to be the same type, and will only check if the values are the same.

// Strict Equality (Type and Value)
console.log(1 === 1); // true
console.log("1" === 1); // false

// Loose Equality (Value)
console.log(1 == 1); // true
console.log("1" == 1); // true | ('1' == '1')
console.log(true == 1); // true | (true == true)

Note: Best practices is to always use strict equality operator because it's more precise and accurate.

Ternary Operators

// If a customer has more than 100 points,
// they are a 'gold' customer, otherwise,
// they are a 'silver' customer.

let points = 110;
let type = points > 100 ? "gold" : "silver";

console.log(type); // gold

Logical Operators

Logical Operator used to make decisions based on multiple conditions.

3 Types of Logical Operators:

  1. AND (&&) returns TRUE if both operands are TRUE.
  2. OR (||) returns TRUE if one of the operands is TRUE, it doesn't matter which one.
  3. NOT (!)
// Logical AND (&&)
console.log(true && true); // true
console.log(false && true); // false

// Building an application for approving loans, so we want to see if the applicant has high income and good credit score then they will be eligible for loan.

let highIncome = true;
let goodCreditScore = true;
let eligibleForLoan = highIncome && goodCreditScore;

console.log(elegibleForLoan); // true
// Logical OR (||)
let highIncome = false;
let goodCreditScore = true;
let eligibleForLoan = highIncome && goodCreditScore;

console.log(elegibleForLoan); // true
// NOT (!)
let highIncome = false;
let goodCreditScore = false;
let eligibleForLoan = highIncome && goodCreditScore;
console.log("Eligible", eligibleForLoan);

let applicationRefused = !eligibleForLoan;

console.log("Application Refused", applicationRefused); // Eligible is false | Application Refused is true

Logical Operators with NonBooleans

In JavaScript, unlike many other programming languages we can use this logical operators with Nonboolean values.

The result of the logical expression is not neccesarily a true or false, that defends on the value of the operands we have.

When a JavaScript engine tries to evaluate a expression, it will look both operands, and if the operands is not a boolean true or false it will try to interpret it as what we call truthy or falsy.

False Values:

  • Falsy (false)
  • undefined
  • null
  • 0
  • false
  • ''
  • Nan is a special value in JavaScript and when we're dealing with mathematical calculation that does not produce a valid number, this value is returned, Not a number.
false || true; // true
false || "Genesis"; // "Genesis"
false || 1; // 1

Anything that is not Falsy -> Truthy

// Short-circuiting
false || 1 || 2; // 1

// As soon as we find an operand that is truthy that operand is returned.
/*
Real World Example:
User has to pick a color or we're gonna use the default color.
Maybe this is the color of the t-shirt they want to buy.
*/

let userColor = "red";
let defaultColor = "blue";
let currentColor = userColor || defaultColor;

console.log(currentColor); // red

// if userColor is set to undefined
console.log(currentColor); // blue

// If the user selected a color (userColor) we're gonna use that otherwise we're gonna use our defaultColor and that determines the currentColor which we're going to display on the screen.

Bitwise Operators

We humans represents decimal system to represent numbers (1 2 3 4 5), but in computers these numbers are stored in a binary format, which is a combination of 0's and 1's.

8 digits: each digit is a bit
8 bits represents 1 byte of information in a computer

Dec. Binary
1 00000001
2 00000010
3 00000011
// Bitwise OR
console.log(1 | 2); // 3

// Bitwise AND
console.log(1 & 2); // 0

When to use Bitwise Operator?

Imagine you want to implement an Access Control System, so the user can have these permissions: Read, Write, Execute.

Now we can use 1 byte of information or 8 bits to determine what kind of permission a user can have.

// Read, Write, Execute
// 00000100 - Read
// 00000010 - Write
// 00000001 - Execute

const readPermission = 4;
const writePermission = 2;
const executePermision = 1;

let myPermission = 0;
myPermission = myPermission | readPermission | writePermission;

let message = myPermission & readPermission ? "yes" : "no";

console.log(message);

Operator Precedence

When working with complex expressions use parenthesis to determine how this operators are applied.

let x = 2 + 3 * 4;
let y = (2 + 3) * 4;

console.log(x); // 14
console.log(y); // 20

Exercise Swapping Variables

Swap the value of these 2 variables.

let a = "red";
let b = "blue";

let c = a;
a = b;
b = c;

console.log(a); // red
console.log(b); // blue

Control Flow

If...else

Two types of Conditional Statements:

  1. If...else
  2. Switch...case
if (condition) statement;
// one statement
else if (anotherCondition) {
  statement;
  statement;
} // 2 or more statement
else statement;

In an application we're going to get the current hour, and depending on it's value we're going to greet the user with the different message.

/*
If hour is between 6am and 12pm: Good morning!
If it is between 12pm and 6pm: Good afternoon!
Otherwise: Good evening!
*/

let hour = 10;

if (hour >= 6 && hour < 12) console.log("Good morning!");
else if (hour >= 12 && hour < 18) console.log("Good afternoon!");
else console.log("Good evening!");

Switch...case

Another way to do a conditional statement.

Syntax structure:

switch (variable) {
  condition:
    statement;
    break;

  default:
    statement;
}

Check the role of the current user:

let role = "guest";

switch (role) {
  case "guest":
    console.log("Guest User");
    break;

  case "moderator":
    console.log("Moderator User");
    break;

  default:
    console.log("Unknown User");
}
// Guest User

You can do it also with if..else statement.

if (role === "guest") console.log("Guest User");
else if (role === "moderator") console.log("Moderator User");
else console.log("Unknown User");

If and else statement is cleaner and shorter, you don't need to worry about the break, default, curly braces.

For

Loops: repeat the action a number of times.

Other types of loops:

  • For
  • While
  • Do...while
  • For...in
  • For...of

Syntax structure:

for (initialExpression; condition; incrementExpression) {
  statement;
}
for (let i = 0; i < 5; i++) {
  console.log("Hello World"); // Print Hello World 5 times
}

Print odd numbers in for loop.

for (let i = 1; i <= 5; i++) {
  if (i % 2 !== 0) console.log(i); // Print odd numbers
}

While

One key difference between a while loop and a for loop is that in for loops, the loop variable is part of the loop itself, but in while loops you have to declare it externally.

Syntax structure:

initialExpress;
while (condition) {
  statement;
  incrementExpression;
}

Print odd numbers in while loop.

let i = 0;
while (i <= 5) {
  if (i % 2 !== 0) console.log(i);
  i++;
}

Do...while

Do...while loops are always excuted at least once, even if the condition is evaluates to false.

let i = 0;
do {
  if (i % 2 !== 0) console.log(i);
  i++;
} while (i <= 5);

Infinite Loops

Infinite loop executes infinitely or forever, it will crash your browser or your computer.

let i = 0;
while (i < 5) {
  console.log(i);
  // i++;
}
while (true) {}
let x = 0;
do {
  // x++;
} while (x < 5);
for (let i = 0; i > 0; i++)

For...in

Two more kinds of loops in javaScript, and we use it to iterate over the properties of an object or elements in an array.

const person {
  name: 'Genesis',
  age: 25
};

for (let key in person)
  console.log(key); // name | age

  console.log(key, person[key]); // name Genesis | age 25
const colors = ["red", "green", "blue"];

for (let index in color) console.log(index); // 0 | 1 | 2

console.log(index, colors[index]); // 0 red | 1 green | 2 blue

For...of

Starting EcmaScript 6 or ES6, we have a new way to iterate over arrays.

const colors = ["red", "green", "blue"];

for (let color of colors) console.log(color); // red, gree, blue

Use the for...in loop to iterate over the properties of an object and for...of loop to iterate over the elements or items in an array.

Break and Continue

There are two key words that can change how the loop behaves.

Break:

let i = 0;
while (i <= 10) {
  if (i === 5) break;
  console.log(i); // 0 | 1 | 2 | 3 | 4
  i++;
}
while (i <= 10) {
  if (i % 2 === 0) {
    i++;
    continue;
  }
  console.log(i); // 1 | 3 | 5 | 7 | 9
  i++;
}

With the break keyword we jump out of the loop and with the continue keyword we jump to the next iteration.

Exercise: Max of Two Numbers

Write a function that takes two numbers and returns the maximum of the two.

let number = max(1, 2);
console.log(number);

function max(x, y) {
  if (x > y) return x;
  return y;

  // cleaner way with same result
  return x > y ? x : y;
}

Exercise: Landscape or Portrait

Write a function isLandscape that takes two parameters width and height of an image and returns true if the image is landscape which means width > height, otherwise it will return false.

console.log(isLandscape(300, 200));

function isLandscape(width, height) {
  // Usual way
  if (width > height) return true;
  return false;

  // Amateur way
  return width > height ? true : false;

  // Professional way
  return width > height;
}

Exercise: FizzBuzz

If the number we pass in is divisible by 3 we get Fizz, then if the number we pass in is divisible by 5 we get Buzz, then if the number we pass in is divisible by 3 and 5 we get FizzBuzz, now if the number is not divisible by either 3 or 5 we're gonna get the same number, and if we didn't pass a number we're gonna get a message Not a number.

const output = fizzBuzz(3);
console.log(output);

function fizzBuzz(input) {
  if (typeof input !== "number") return NaN;

  if (input % 3 === 0 && input % 5 === 0) return "FizzBuzz";

  if (input % 3 === 0) return "Fizz";

  if (input % 5 === 0) return "Buzz";

  return input;
}

Exercise: Demerit Points

Implement this function checkSpeed that takes parameter speed of the car. In this program assume that speed limit is 70km/h, so if the car is driving under this speed limit we get Ok message in the console. Similarly if we pass 70 it's still good we still get the Ok message. Now for every 5kms above the speed limit we get 1 point. As for the calculation use one of the built in functions in javaScript that is Math.floor() where you give it a floating point number and it will convert it to the greatest integer. Lastly If the driver gets more than 12 point (180kms/h) then the license will be suspended, print License suspended.

/*
Speed Limit = 70km/h
5 -> 1 point
Math.floor(1.3)
12 point -> suspended
*/
checkSpeed(130);

function checkSpeed(speed) {
  const speedLimit = 70;
  const kmPerPoint = 5;

  if (speed < speedLimit + kmPerPoint) {
    console.log("Ok");
    return;
  }

  const points = Math.floor((speed - speedLimit) / kmPerPoint);
  if (points >= 12) console.log("License suspended");
  else console.log("Points", points);
}

Execise: Even and Odd Numbers

Write a function showNumber that takes a parameter limit. When we pass a number like 10, we should see the number from 0 up to the number we applied as the limit. Next to each number should display if it's EVEN or ODD label.

showNumbers(10);

function showNumbers(limit) {
  for (let i = 0; i <= limit; i++) {
    if (i % 2 === 0) console.log(i, "EVEN");
    else console.log(i, "ODD");

    // much cleaner way
    const message = i % 2 === 0 ? "EVEN" : "ODD";
    console.log(i, message);
  }
}

Exercise: Count Truthy

Create a function countTruthy that takes an array and returns the number of truthy elements in this array.

const array = [1, 2, 3];
console.log(countTruthy(array));

function countTruthy(array) {
  let count = 0;
  for (let value of array) if (value) count++;
  return count;
}

Exercise: String Properties

Create a function showProperties that takes an object obj, and this function should display all the properties of this object that are type of string.

const movie = {
  title: "Unbelivable movie",
  releaseYear: 2018,
  rating: 4.5,
  director: "Unbelievable director"
};

showProperties(movie);

function showProperties(obj) {
  let count = 0;
  for (let key in obj) {
    if (typeof obj[key] === "string") console.log(key, obj[key]);
  }
}

Exercise: Sum of Multiples of 3 and 5

Create a function sum that takes a limit parameter, the function should return the of all multiples of 3 and 5 from 0 up to this limit.

console.log(sum(10));

function sum(limit) {
  let sum = 0;

  for (let i = 0; i <= limit; i++) if (i % 3 === 0 || i % 5 === 0) sum += i;

  return sum;
}

Exercise: Grade

Calculate the grade of the student. The function calculateGrade should calculates the average grade.

const marks = [80, 80, 50]; // 210 / 3

/*
Average = 70;
0-59: F
60-69: D
70-79: C
80-89: B
90-100: A
*/

console.log(calculateGrade(marks));

function calculateGrade(marks) {
  const average = calculateAverage(marks);

  if (average < 60) return "F";
  if (average < 70) return "D";
  if (average < 80) return "C";
  if (average < 90) return "B";
  return "A";
}

function calculateAverage(array) {
  let sum = 0;
  for (let value of array) sum += value;
  return sum / array.length;
}

Exercise: Stars

Create a function showStars that takes rows as a parameter. When we call the function it should display the star and the number of stars in that row depends on the row we are in.

showStars(5);

function showStars(rows) {
  let stars = 0;
  for (let row = 1; row <= rows; row++) {
    let pattern = "";
    for (let i = 0; i < row; i++) {
      pattern += "*";
    }
    console.log(pattern);
  }
}

Exercise: Prime Numbers

Create a function showPrimesthat takes limit as a parameter, and shows all the prime numbers of this limit.

// Prime (whose factors are only 1 and itself)
// 11 = 1, 11
// 13 = 1, 13

// Composite (Can be divided evenly by its factors)
// 12 = 1, 2, 3, 4, 6, 12

showPrimes(20);

function showPrimes(limit) {
  for (let number = 2; number < limit; number++)
    if (isPrime(number)) console.log(number);
}

function isPrime(number) {
  for (let factor = 2; factor < number; factor++)
    if (number % factor === 0) return false;
  return true;
}

Objects

Basics

Objects are collection of key value pairs, so if you have properties that are highly related, you wanna encapsulate them inside of an object

Example: We're building an application for drawing different kinds of shapes like: cirlcles, rectagles and so on.. we could declare multiple variables around circle, and they are highly related they will present the circle, but a better approach is to put this variables inside of an object.

/*
let radius = 1;
let x = 1;
let y = 1;
*/

function draw() {}
function move() {}

// Instead of using this you can do it inside of an object
const circle = { // Object literal
  radius: 1,
  location: { // Object
    x: 1,
    y: 1
  },
  isVisible: true // Boolean
  draw: function() { // function (method)
    console.log('draw');
  }
};

circle.draw(); // Method

If a function is part of an object, in Object-oriented Programming terms, we refer to that function as a method.

Factory Functions

Naming convention for factory function is camelCase.

camelCase Notation: oneTwoThreeFour

function createCircle(radius) {
  return {
    radius, // radius
    draw() {
      console.log("draw");
    }
  };
}

const circle1 = createCircle(1);
console.log(circle1);

const circle2 = createCircle(2);
console.log(circle2);

We Defined the logic in one place so we can call this function with different values or different arguments we get different circle objects that we have defined the draw method only in one place, so if there is bug in this method that we need to fix in the future, there is a single place that we need to modify.

Factory functions are not the only way to create an object, we can also use constructor functions.

Constructor Functions

The naming convention for the constructor functions is different we use Pascal notation.

Pascal Notation: OneTwoThreeFour

function Circle(radius) {
  this.radius = radius;
  this.draw = function() {
    console.log("draw");
  };
}

const circle = new Circle(1);

When we use the new operator three things happen:

  1. creates an empty object
  2. set the this to point to this object
  3. return that object from the function

Difference between the factory function and constructor functions:

In factory functions we simply call a function and in this function we return a new object, in contrast with constructor functions we use a new operator and instead of returning an object we use the this keyword.

In terms of naming convention, factory functions use a camel notation convention and constructor functions use a pascal notation convention.

Dynamic Nature of Objects

Once you create a new object, you can always add a new properties or methods, or remove existing ones.

const circle = {
  radius: 1
};

circle.color = "yellow"; // Add a property
circle.draw = function() {}; // Add a method

delete circle.color;
delete circle.draw;

console.log(circle);

When we use constant here that means we cannot reassign the variable. We cannot reset the circle = {} to a new object.

Constructor Property

Every object in javaScript has a property called constructor and that references the function that was use to construct or create an object.

// Factory Function
function createCircle(radius) {
  return {
    radius, // radius
    draw() {
      console.log("draw");
    }
  };
}
const circle = createCircle(1);

// Constructor Function
function Circle(radius) {
  this.radius = radius;
  this.draw = function() {
    console.log("draw");
  };
}
const another = new Circle(1);

Output using contructor property

another.constructor;
/*
f Circle(radius) {
  this.radius = radius;
  this.draw = function() {
    console.log('draw');
  }
}
*/

circle.constructor;
// f Object() { [native code] }
let x = {}; // Object literal

When using object literal, javaScript engine will translate that into something like this:

let x = new Object();

Few built in constructor in javaScript:

Constructor Literal
new String(); '', "", ``
new Boolean(); true, false
new Number(); 1, 2, 3, 4

Functions are Objects

Internally JavaScript will use Function constructor to create the object.

function Circle(radius) {
  this.radius = radius;
  this.draw = function() {
    console.log("draw");
  };
}

// When we declare a function, internally it represented like this.
const Circle1 = new Function(
  "radius",
  `
this.radius = radius;
this.draw = function() {
  console.log('draw');
}
`
);

const circle = new Circle1(1);

first argument is thisArg, passing an empty object, this will reference to the object that we the pass in, then the add the argument explicitly.

Circle.call({}, 1);
// both are exactly the same
const another = new Circle(1);

When use the new operator, this new operator will internally create an empty object and pass them as the first argument to the call method, and this object will determine the context or this, so this will reference the object.

If we don't use the new operator, this by default will point to the global object which is window.

Another method called apply(), it's exactly like the call() method with this we can call a function, but instead of passing all the arguments explicitly, we pass them in an array. So this is useful if you already have an array somewhere else in your application and you want to pass an array as the 2nd argumentto apply() method.

circle.apply({}, [1, 2, 3]);

Value vs Reference Types

Value Types:
  1. Number
  2. String
  3. Boolean
  4. Symbol
  5. undefined
  6. null
Reference Types:
  1. Object
  2. Function
  3. Array

When we work with Primitive the value is stored inside the variable, when we copy that variable, that value that is stored in the variable is copied into the new variable. So they completely independent to each other.

let x = 10;
let y = x;

x = 20;

// Log
x; // 20
y; // 10

When we use an object, that object is not stored in the variable, that object is stored somewhere in the memory, and the address of that memory location is stored inside that variable.

let x = { value: 10 };
let y = x;

x.value = 20;

// log
x; // {value: 20}
y; // {value: 20}

In other words, both x and y are pointing to the same object in memory and when we modify that object using either x or y, it changes are immediately visible to the other variable.

Conclusion:
Primitives are copied by their value, Reference types or Objects are copied by their reference.

Primitive

let number = 10;

function increase(number) {
  number++;
}

increase(number);
console.log(number); // 10

Reference

let obj = { value: 10 };

function increase(obj) {
  obj.value++;
}

increase(obj);
console.log(obj); // {value: 11}

Enumerating Properties of an Object

for...of loop can only be use with iterables such as arrays and maps.

const circle = {
  radius: 1,
  draw() {
    console.log("draw");
  }
};

for (let key in circle) console.log(key, circle[key]); // raidus 1 | draw f | draw() { console.log('draw'); }

for (let key of circle) console.log(key); // Uncaught TypeError: circle is not iterable

Object is not iterable, so we cannot iterate it using a for...of loop, however we have this method Object.keys()

for (let key of Object.keys(circle)) console.log(key); // radius | draw

Similar method to Object.keys()

for (let entry of Object.entries(circle)) console.log(entry); // (2) ["radies", 1] | (2) ["draw", f]
// each entry is an array

To see if the given property or method exist in an object.

if ("radius" in circle) console.log("yes");

The simplest way to enumerate the properties of an object is using the for...in loop.

Cloning an Object

const circle = {
  radius: 1,
  draw() {
    console.log("draw");
  }
};

const another = {};
for (let key in circle) another[key] = circle[key];
// equivalent
another["radius"] = circle["radius"];

console.log(another); // {radius: 1, draw: f}

But this approach for copying or cloning an object is a little bit old, in modern javaScript we have a better ways to achieve the same thing.

const another = Object.assign({}, circle);

Yet another, simpler and more elegant way to clone an object.

const another = { ...circle };

Object.assign() copies the properties and methods from one or more source objexts into target object and we can use that to clone an object or combine mulptiple objects into a single object, and the spread operator is use to spread an object which basically means getting all its properties and methods and put in them into another object.

Garbage Collection

In low-level languages like C or C++ when creating an object, we need to allocate memory to it, and when we're done we need to deallocate memory, but in JavaScript we don't have this concept, we can easily create a new object, at the time we initialize this object the memory is automatically allocated to this object, next we can use that, and when we're done using it, we don't need to deallocate the memory.

So, JavaScript engine has Garbage Collector, the job of this garbage collector is to find the variables or constants that are no longer use, and then deallocate the memory that was allocated to them earlier.

let circle = {};
console.log(circle);

Math

Math is a built-in object that has properties and methods for mathematical constants and functions.

Properties:
Math.PI ration of the circumference of a circle to its diameter, approximately 3.14159.

Methods:
Math.abs(x) returns the absolute value of a number. (If you give it a negative number, it returns its positive value.)

Math.floor(x) returns the largest integer less than or equal to a number.

Math.random() returns a pseudo-random number between 0 and 1.

Math.round(x) returns the value of a number rounded to the nearest integer.

Math.max([x[, y[,...]]]) returns the largest of zero or more numbers.

Math.min([x[, y[,...]]]) returns the smallest of zero or more numbers.

String

String object is a constructor for strings or a sequence of characters.

JavaScript has two kinds of strings:

// String primitive
const message = 'hi';

// String object
const another = new String('hi')

However, when we use the dot notation with a string primitive, javaScript engine internally wraps this with a string object. We can't see it, but we can work with it like a string object.

Escape Notation is use to encode special characters beside regular, printable characters.

const message = 'This is my first message';

// returns the number of characters in the string
message.length // 24 

// Determines whether one string may be found within another string
message.includes('my') // true

// Determines whether a string begins with the characters of another string.
message.startsWith('This') // true

// Determines whether a string ends with the characters of another string.
message.ensWith('e') // true

// Returns the index within the calling String object of the first occurrence of the specified value, or -1 if not found.
message.indexOf('my') // 8

// replace the matched substring with a new substring.
message.replace('first', 'second') // "This is my second message"

// Returns the calling string value converted to uppercase.
message.toUpperCase() // "THIS IS MY FIRST MESSAGE"

// Trims whitespace from the beginning and end of the string. Part of the ECMAScript 5 standard.
message.trim() // "This is my first message"

// Splits a String object into an array of strings by separating the string into substrings.
message.split(' ') // ["This", "is", "my", "first", "message"]
Code Output
\' single quote
\" double quote
\\ backslash
\n new line
\\ backslash
\t tab

Template Literals

Template literals are string literals allowing embedded expressions. You can use multi-line strings and string interpolation features with them.

// String literal
const message = 
'This is my\n' + 
'\'first\' message';

// Template literal
const another = `This is my
first message`;

We can also add placeholders with template literals.

const name = 'John';

// Regular string
const message = 'Hi ' + name + ',\n';

// Template literal string
const message = `Hi ${name},`

Date

Date objects are based on a time value that is the number of milliseconds since 1 January 1970 UTC.

Different ways to create a Date object:

const now = new Date() // connstructor function
const date1 = new Date('May 11 2018 19:00');
const date2 = new Date(2018, 4, 11, 9)

now.get
now.set
now.setFullYear(2017);

All these Date objects have a bunch of get and set methods.

Exercise 1: Address Objects

Create an address object with three properties: street, city, and zipcode. After that create a function called showAddress that takes an address object and display all the properties in this object along with their value.

const address = {
  street: 'San Jose',
  city: 'San Fernano',
  zipCode: 2000
};

function showAddress(address) {
  for (let key in address)
    console.log(key, address[key]);
}

showAddress(address);

Exercise 2: Factory and Constructor Function

Write two different functio to initialize an address object

  1. Using a Factory function
  2. Using Constructor function
// Factory function
let address = createAddress('San Jose', 'San Fernando', 2000);
console.log(address);

function createAddress(street, city, zipCode) {
  return {
    street,
    city,
    zipCode
  }
}
// Constructor function
let address = new Address('San Jose', 'San Fernando', 2000);
console.log(address);

function Address(street, city, zipCode) {
  this.street = street;
  this.city = city;
  this.zipCode = zipCode;
}

Exercise 3: Object Equality

Create a two functions and pass two address object, which are address1 and address2.

  1. areEqual function checks if all the properties of these two objects are equal, this function should return true, otherwiise it should return false
  2. areSame function tells us if address1 and address2 are pointing to the exact same object. In other words, they are referencing to the same object.
let address1 = new Address('San Jose', 'San Fernando', 2000);
let address2 = new Address('San Jose', 'San Fernando', 2000);
let address3 = address1;

console.log(areEqual(address1, address2));
console.log(areSame(address1, address2));
console.log(areSame(address1, address3));

function Address(street, city, zipCode) {
  this.street = street;
  this.city = city;
  this.zipCode = zipCode;
}

function areEqual(address1, address2) {
  return address1.street === address2.street &&
    address1.city === address2.city &&
    address1.zipCode === address2.zipCode;
}

function areSame(address1, address2) {
  return address1 === address2;
}

Exercise 4: Blog Post Object

Create a blog post object with these properties:

  • title
  • body
  • author
  • views
  • comment
    • author
    • body
  • isLive
let post = {
  title: 'Web Development',
  body: 'Learning how to code is very hard, the chalenges we are facing are very helpful that we can still learn it.',
  author: 'Genesis Gabiola',
  views: 1021,
  comment: [
    { author: 'Abbyjel Mantibe', body: 'This post is very helpful, I learned a lot.' },
    { author: 'David Arcilla', body: 'Good stuff.' },
  ],
  isLive: true
};

console.log(post);

Exercise 5: Constructor Functions

Imagine that we are building a blogging engine, the user is drafting a post, but they haven't published it yet. What should the construction function should look like?

let post = new Post('Web Development', 'Learning how to code is very hard, the chalenges we are facing are very helpful that we can still learn it.', 'Genesis Gabiola')

console.log(post);

function Post(title, body, author) {
  this.title = title;
  this.body = body;
  this.author = author;
  this.views = 0;
  this.comments = [];
  this.isLive = false;
}

Exercise 6: Price Range Objects

Create an array of objects, each object is what we called a price range object. Create at least 3 price range objects. Think about the properties that each of object should have.

let priceRanges = [
  { label: '$', tooltip: 'Inexpensive', minPerPerson: 0, maxPerPerson: 10 },
  { label: '$$', tooltip: 'Moderate', minPerPerson: 11, maxPerPerson: 20 },
  { label: '$$$', tooltip: 'Expensive', minPerPerson: 21, maxPerPerson: 50 },
];

let restaurants = [
  { averagePerPerson: 5 }
]

Arrays

Adding Elements

Using constant variable you cannot reassign it to something else, however it's perfectly fine to modify the content of the array. You can add new elements or remove existing elements.

Three methods to add new elements in an array:

push() method adds one or more elements to the end8 of an array and returns the new length of the array.

unshift() method adds one or more elements to the beginning of an array and returns the new length of the array.

splice() method changes the contents of an array by removing existing elements and/or adding new elements.

const numbers = [3, 4];

// End
numbers.push(5, 6); // (4) [3, 4, 5, 6]

// Beginning
numbers.unshift(1, 2); // (6) [1, 2, 3, 4, 5, 6]

// Middle
numbers.splice(2, 0, 'a', 'b') // (8) [1, 2, "a", "b", 3, 4, 5, 6]

console.log(numbers);

Finding ELements(Primitives)

indexOf() we pass the element we are looking for and if that element exist in that array, this method will return the index of that element in the array, if it doesn't exist will return -1.

lastIndexOf() that will return the last index of the given element or -1 if it doesn't exist.

includes() this simply returns true if the given element exist in the array.

All the methods here have a second parameter which is optional, and that is the starting index where the search will begin.

const numbers = [1, 2, 3, 1, 4];

console.log(numbers.indexOf(3)); // 1
console.log(numbers.indexOf('a')); // -1
console.log(numbers.lastIndexOf(1)); // 3
console.log(numbers.indexOf(1) !== -1); // true
console.log(numbers.includes(1)); // true
console.log(numbers.indexOf(1, 2)); // 3

Finding Elements (Object) - Reference Types

find() method returns the value of the first element in the array that satisfies the provided testing function. Otherwise undefined is returned.

findIndex()method returns the index of the first element in the array that satisfies the provided testing function. Otherwise, it returns -1, indicating no element passed the test.

const courses = [
  { id: 1, name : 'a' },
  { id: 2, name : 'b' },
];

console.log(courses.includes({ id: 1, name: 'a' })) // false

const course = courses.find(function(course) {
  return course.name === 'a'; // {id: 1, name: "a"}
  return course.name === 'xyz'; // undefined
});


const course = courses.findIndex(function(course) {
  return course.name === 'b'; // 1
  return course.name === 'xyz'; // -1
});

console.log(course); 

Arrow Functions

Arrow Function expression has a shorter syntax than a function expression and does not have its own this, arguments, super, or new.target. These function expressions are best suited for non-method functions, and they cannot be used as constructors.

Whenever you wan to pass a function as a callback function or as argument for different method, you can use the arrow function syntax.

const courses = [
  { id: 1, name : 'a' },
  { id: 2, name : 'b' },
];


const course = courses.find(function(course) {
  return course.name === 'a'; // {id: 1, name: "a"}
});

// Arrow function
const course = courses.find(course => course.name === 'a';); // {id: 1, name: "a"}

console.log(course);

Removing Elements

pop() method removes the last element from an array and returns that element. This method changes the length of the array.

shift() method removes the first element from an array and returns that removed element. This method changes the length of the array.

splice() method changes the contents of an array by removing existing elements and/or adding new elements.

const numbers = [1, 2, 3, 4];

// End
const last = numbers.pop();
console.log(numbers); // (3) [1, 2, 3]
console.log(last); // 4

// Beginning
const first = numbers.shift();
console.log(numbers); // (2) [2, 3]
console.log(first); // 1

// Middle
numbers.splice(2, 1); // (3) [1, 2, 4]
numbers.splice(2, 2); // (2) [1, 2]
console.log(numbers)

Emptying an Array

There are few different resolution in emptying an array.

let numbers = [1, 2, 3, 4];
// let another = numbers;

// Solution 1
numbers = []; // []
console.log(another) // (4) [1, 2, 3, 4]

// Solution 2
numbers.length = 0; // []
console.log(another) // []

// Solution 3
numbers.splice(0, numbers.length) // []

// Solution 4
while (numbers. length > 0)
  numbers.pop(); // []

console.log(numbers)

Note:
The old array is still in the memory, so if there's no other reference to the object eventually it will be remove by the garbage collector.

So, if we have another variable pointing to the same object, the original array will not be garbage collected.

If we reassign the variable, that variable is pointing to the new object in memory, but another new variable is still pointing to the old object.

Combining and Slicing Array

concat() method is used to merge two or more arrays. This method does not change the existing arrays, but instead returns a new array.

slice() method returns a shallow copy of a portion of an array into a new array object selected from begin to end (end not included). The original array will not be modified.

const first = [1, 2, 3];
const second = [4, 5, 6];

const combined = first.concat(second);

const slice = combined.slice(2, 4);

console.log(combined); // (6) [1, 2, 3, 4, 5, 6]
console.log(slice); // (2) [3, 4]

The Spread Operator

There is another way to combine two arrays using spread operator in ES6.

Spread syntax allows an iterable such as an array expression or string to be expanded in places where zero or more arguments (for function calls) or elements (for array literals) are expected, or an object expression to be expanded in places where zero or more key-value pairs (for object literals) are expected.

const first = [1, 2, 3];
const second = [4, 5, 6];

const combined = [...first, ...second];

// const copy = combined.slice(2, 4);
const copy = [...combined];

Iterating an Array

Iterate an array using for...of or

forEach() method executes a provided function once for each array element.

const numbers = [1, 2, 3];

// for...of
for (let number of numbers)
  console.log(number); // 1 | 2 | 3

// forEach
numbers.forEach(function(number) {
  console.log(number); // 1 | 2 |3
});

// forEach() arrow function
numbers.forEach(number => console.log(number)); // 1 | 2 | 3

// forEach() with index
numbers.forEach((number, index) => console.log(index, number)); // 0 1 | 1 2 | 2 3

Note that the callback function that we pass to the forEach() method can optionally take a second parameter and that's the index.

You can't get index with for...of, but if you need that you can use for...in loop

Joining Arrays

join() method creates and returns a new string by concatenating all of the elements in an array (or an array-like object), separated by commas or a specified separator string.

const numbers = [1, 2, 3];
const joined = numbers.join(',');
console.log(joined); // 1,2,3


const message = 'This is my first message';
const parts = message.split(' ');
console.log(parts); // (5) ["This", "is", "my", "first", "message"]



const combined = parts.join('-');
console.log(combined); // This-is-my-first-message

This technique is particularly useful when building a URL slug, you cannot have white space in URLs, so all the white spaces are replace with an hyphen. So as part of converting the title to a URL slug, you'll have to split the string perhaps throughout some of the words maybe replace some words with other words and then eventually combine them using a hyphen

Sorting Arrays

sort() method sorts the elements of an array in place and returns the array.

reverse() method reverses an array in place. The first array element becomes the last, and the last array element becomes the first.

const numbers = [2, 3, 1];
numbers.sort();
console.log(numbers); // (3) [1, 2, 3]

numbers.reverse();
console.log(numbers) // (3) [3, 2, 1]

sort() method is pretty useful when you have numbers and a strings in an array, but when you objects, it doesn't work by default, you need to do some extra work.

const courses = [
  { id: 1, name: 'Node.js' },
  { id: 2, name: 'JavaScript' },
];

courses.sort(function(a, b) {
  /*
  a < b => -1
  a > b => 1
  a === b => 0
  */

  // exclude case sensitivity when comparing these name
  const nameA = a.name.toUpperCase();
  const nameB = b.name.toUpperCase();

  if (nameA < nameB) return -1;
  if (nameA > nameB) return 1;
  return 0;
});
console.log(courses);

Each character in a computer is internally represented using a number.

ASCII(American Standard Code for Information Interchange) a standard way of representing numbers and letters in a computer file so that they can be read by most computers.

Testing the Elements of an Array

every() method tests whether all elements in the array pass the test implemented by the provided function. Returns true for any condition put on an empty array.

some() method tests whether at least one element in the array passes the test implemented by the provided function. Returns false for any condition put on an empty array.

const numbers = [1, -1, 2, 3];
// every() method
const allPositive = numbers.every(function(value) {
  return value >= 0;
});
console.log(allPositive);
// some() method
const atLeastOnePositive = numbers.some(function(value) {
  return value >= 0;
});
console.log(atLeastOnePositive);

Filtering an Array

filter() method creates a new array with all elements that pass the test implemented by the provided function.

const numbers = [1, -1, 2, 3];

// call back function
const filtered = numbers.filter(function(value) {
  return value >= 0;
});

console.log(filtered);
// arrow function | value 
const filtered = numbers.filter(n => n >= 0);

Here we are dealing with an array of numbers, in a real world application, you would be using array of objects.

For example, in yelp website we can clickon the button Open Now to find the only restaurants that are open now, that's one kind of filtering. If you want to implement something like that, instead of an array of numbers, you would have an array of restaurants and each restaurant has opening hours, so you could filter restaurants that are currently open and return them.

Mapping an Array

map() method creates a new array with the results of calling a provided function on every element in the calling array.

mapping the numbers to strings:

const numbers = [1, -1, 2, 3];

const filtered = numbers.filter(n => n >= 0);

const items = filtered.map(n => '<li>' + n + '</li>'); // (3) ["<li>1</li>", "<li>2</li>", "<li>3</li>"]

const html = '<ul>' + items.join('') + '</ul>'; // <li>1</li> <li>2</li> <li>3</li>

console.log(html);

mapping to objects:

const numbers = [1, -1, 2, 3];

const filtered = numbers.filter(n => n >= 0);

/*
const items = filtered.map(n => {
  return { value: n };
}); // (3) [{...}, {...}, {...}]

const items = filtered.map(n => { value: n }); // (3) [undefined, undefined, undefined]
*/

const items = filtered.map(n => ({ value: n })); // (3) [{...}, {...}, {...}]

console.log(items);

Both filter() and map() method return a new array, they don't modify the original array. These methods are chainable, which means we can call them one afer another in a chain.

// Chaining
const items = numbers
  .filter(n => n >= 0)
  .map(n => ({ value: n }))
  .filter(obj => obj.value > 1) // calling additional filter
  .map(obj => obj.value); // Again calling the map method | map each object to a number

console.log(items);

Reducing an Array

To calculate the sum of all the numbers in the array, this is also similar to calculating the total cost of all the item in the shopping cart.

So each number here will represent the price of an item in the shopping cart.

const numbers = [1, -1, 2, 3];
let sum = 0;
for (let n of numbers)
  sum += n;

console.log(sum);

There is a cleaner and more elegant way to write the same code, using the reduce() method in arrays.

reduce() method executes a reducer function (that you provide) on each member of the array resulting in a single output value.

/*
Here's what's happening in the background
a = 0, c = 1 => a = 1
a = 1, c = -1 => a = 0
a = 0, c = 2 => a = 2
a = 2, c = 3 => a = 5
*/

const sum = numbers.reduce((accumulator, currentValue) => {
  return accumulator + currentValue;
}, 0);
console.log(sum);

Much shorter code:

const sum = numbers.reduce(
  (accumulator, currentValue) => accumulator + currentValue
);
console.log(sum);

Exercise 1: Array from Range

Write a function arrayFromRange that takes two parameters min and max

const numbers = arrayFromRange(-10, -4);
console.log(numbers); // (7) [-10, -9, -8, -7, -6, -5, -4]

function arrayFromRange(min, max) {
  const output = [];
  for (let i = min; i < max; i++)
    output.push(i);
  return output;
}

Exercise 2: Includes

Create a function called include that takes an array and searchElement, if we have the search element in the array return true, otherwise return false.

const numbers = [1, 2, 3, 4];
console.log(numbers.includes(1));

function includes(array, searchElement) {
  for (let element of array)
    if (element === searchElement)
      return true;
  return false;
}

Exercise 3: Except

We have a numbers array and a function called except that takes an array (numbers)`, and another array. And with another array we can add the values we want to be excluded from the original array. So the function should return a new array without the values that we have pass in another array.

const numbers = [1, 2, 3, 4];
const output = except(numbers, [1]); // (3) [2, 3, 4]
const output = except(numbers, [1, 2]); // (2) [3, 4]
console.log(output);

function except(array, excluded) {
  const output = [];
  for (let element of array)
    if (!excluded.includes(element))
      output.push(element);
  return output;
}

Exercise 4: Moving an Element

We have this function called move and we use that to move an element in an array. So we give it an array, index of an element we want to move, and an offset. If we pass more than the length of an array in offset, we get an error Invalid offset. To display an error use the console.error() method.

Note that offset can be negative number, and the original array is unaffected, so we get a new array with items reorder.

const numbers = [1, 2, 3, 4];
const output = move(numbers, 0, 1);

console.log(output);

function move(array, index, offset) {
  const position = index + offset;
  if (position >= array.length || position < 0) {
    console.error('Invalid offset.');
    return;
  }

  const output = [...array];
  const element = output.splice(index, 1)[0];
  output.splice(position, 0, element);
  return output;
}

Exercise 5: Count Occurences

Create a function called countOccurences that takes an array and a searchElement and returns the number of times that searchElement has occured in this array, otherwise it will return 0 if it didn't occur the searchEleemnt in the array.

const numbers = [1, 2, 3, 4, 1];
const count = countOccurences(numbers, 1); // 2
console.log(count);

function countOccurences(array, searchElement) {
  /*
  let count = 0
  for (let element of array)
    if (element === searchElement)
      count++;
  return count;
  */

  // reduce() method
  return array.reduce((accumulator, current) => {
    const occurence = (current === searchElement) ? 1 : 0;
    console.log(accumulator, current, searchElement)
    return accumulator + occurence;
  }, 0);
}

Exercise 6: Get Max

Create a function getMax that takes an array as a parameter and it returns that largest number in that array, if we give it an empty array we get undefined.

const numbers = [1, 2, 3, 4];
const max = getMax([1, 3, 4, 6]);
console.log(max);

function getMax(array) {
  if (array.length === 0)
    return undefined;
  /*
  let max = array[0];
  for (let i = 1; i < array.length; i++)
    if (array[i] > max)
      max = array[i];
  return max;
  */

  // reduce() method
  return array.reduce((accumulator, current) => {
    /*
    if (current > accumulator)
      return current;
    return accumulator;
    */

    // Conditional operator
    return (accumulator > current) ? accumulator : current;
  });
}

Much shorter code:

function geMax(array) {
  if (array.length === 0) return undefined;
  return array.reduce((a, b) => (a > b) ? a : b);
}

Exercise 7: Movies

  1. Write code the get all the movies in 2018 with rating > 4.
  2. Sort them by their rating in descending order.
  3. Pick only their title property and display in to the console.
  4. Result should 'b' and 'a'
const movies = [
  { title: 'a', year: 2018, rating: 4.5 },
  { title: 'b', year: 2018, rating: 4.7 },
  { title: 'c', year: 2018, rating: 3 },
  { title: 'd', year: 2017, rating: 4.5 },
];

const titles = movies
  .filter(m => m.year === 2018 && m.rating >= 4)
  .sort((a, b) => a.rating - b.rating)
  .reverse()
  .map(m => m.title)

console.log(titles);

Functions

Function Declaration vs Expressions

There are two ways to define a function.

  1. Function Declarion
function walk() {
  console.log('walk');
}
  1. Function Expression
// Named Function Expression
let run = function walk() {};

// Anonymous Function Expression
let run = function() {
  console.log('run');
};
let move = run;
run(); // run
move(); // run

Hoisting

The key difference between Function Declaration and Function Expression is that we can call the function declaration syntax before it is define, but we cannot do this with function expression.

  1. Function Declarion
walk();

function walk() {
  console.log('walk'); // walk
}
  1. Function Expression
run();

const run = function run() {
  console.log('run'); // Uncaught ReferenceError: run is not defined
};

This is exactly like using a variable or constant before it's define.

Reason for this is because when javaScript engine executes this code it moves all the function declarations to the top, so our code will look like this in run time:

function walk() {
  console.log('walk'); // walk
}

walk();

Hoisting is the process of moving function declarations to the top of the file, and this is done automatically by the javaScript engine that is executing the code.

Arguments

JavaScript is a dynamic language so we can declare a variable set it to number, and then change it's type and set it to string. This is perfectly fine from the perspective of the javaScript engine. This is one of the characteristics of the dynamic language.

let x = 1;
x = 'a';

We have the same concept in the arguments of a function.

function sum(a, b) {
  return a + b;
}
console.log(sum(1, 2)); // 3

But in javaScript it doesn't matter that this function has 2 two parameters, we can pass one argument, an we get a NaN (Not a number), because here a is 1 and b by default is undefined. So when we have 1 + undefined the result is NaN. this is not a valid arithmetic operation.

The same happen if we don't pass any arguments, we don't get an error, get NaN as a result.

console.log(sum(1, 2, 3, 4, 5)) // 3

By the same token we can pass additional arguments, so in this case we passed five arguments, and this is perfectly valid javaScript code, but we still get 3, because only the first two arguments are used.

But when we want to have a varying number of parameters in a function, or flexibility to pass as many arguments as we want and get their sum.

Every function in javaScript has a special object called arguments

console.log(arguments);

/*
Arguments(5) [1, 2, 3, 4, 5, calle: f, Symbol (Symbol.iterator): f]
0: 1
1: 2
2: 3
3: 4
4: 5
calle: f sum(a, b)
length: 5
*/

We can see that it kinda looks like an array, but it's an array, it's an object. But the keys we have for this object are indexes of the arguments passed to this function.

Changing the function to return the sum of all the arguments.

We can use the for...of loop on arrays, but technically this loop can be use on any object that has an iterator.

// function sum(a, b) {
function sum() {
  let total = 0;
  for (let value of arguments)
    total += value;
  return total;
}
console.log(sum(1, 2, 3, 4, 5, 10)); // 25

Interesting thing is we can completely remove the parameters, because technically we are working with the arguments object to access the arguments that has passed.

We are not referencing a or b in the body of the function, so we can simplify the code and remove the parameters.

The Rest Operator

In modern javaScript, if you want to have a varying number of parameters, we can use the rest operator.

function sum(...args) {
  let total = 0;
  for (let value of arguments)
    total += value;
  return total;
}
console.log(sum(1, 2, 3, 4, 5, 10)); // 25

Don't confuse this with spread operator, because both are exact look the same.

Spread operator we can spread an array, which means taken it's individual elements, but when we this along with the parameter of the function we refer to this as the rest operator.

function sum(...args) {
  console.log(args);
}
console.log(sum(1, 2, 3, 4, 5, 10)); // (6) [1, 2, 3, 4, 5, 10]

When we apply the rest operator to a parameter of the function, we can pass varying number of arguments and the rest operator will take all of them and put them in an array.

Now if we want to get the sum of all the numbers in an array, we can use the reduce() method.

function sum(...args) {
  return args.reduce((a, b) => a + b);
}
console.log(sum(1, 2, 3, 4, 5, 10));

Imagine we're gonna use this function to calculate the total costs of items in a shopping cart, and have a discount factor.

function sum(discount, ...prices) {
  const total = prices.reduce((a, b) => a + b);
  return total * (1 - discount);
}
console.log(sum(0.1, 20, 30));

We cannot have a parameter after using the rest operator.

Default Parameters

There are times we want to supply default values to the parameters of a function.

Imagine a function for calculating the total interest.

function interest(principal, rate, years) {
  return principal * rate / 100 * years;
}

console.log(interest(10000, 3.5, 5)); // 1750

What if we want to have a default values for interest rate and number of years.

rate = rate || 3.5;
years = years || 5;

Cleaner way to achieve the same thing.

function interest(principal, rate = 3.5, years = 5) {
  return principal * rate / 100 * years;
}

console.log(interest(10000, 3.5, 5));

If one of the parameter is not set it's value, put undefined in the argument, but this is not advisable because it will make your code look ugly better to define its value in parameter.

Gettters and Setters

Imagine a person object with two properties, firstName and lastName. Somewhere in the application we want to display a person's full name.

const person = {
  firstName: 'Genesis',
  lastName: 'Gabiola'
};

// console.log(person.firstName + ' ' + person.lastName);

// template literal
console.log(`${person.firstName} ${person.lastName}`);

However, the problem with this approach is that maybe there are multiple places in our application where we want to display someone's full name. With the current implementation we will have to repeat the template literal in multiple places.

A better approach is to define a method in the object call full name, wheneve we want to display the person's full name we simply call that method.

const person = {
  firstName: 'Genesis',
  lastName: 'Gabiola',
  fullName() {
    return `${person.firstName} ${person.lastName}`;
  }
};

console.log(person.fullName());

However, there are couple of problems with this approach.

  1. First Problem is that person.fullName() is read-only, in other words, we cannot set the person.fullName() from the outside.

  2. Not want to call it like a method, it will be nice if we can treat it like a property so we can drop the parenthesis like this person.fullName.

getters => to access the properties of an object

setters => to change or mutate them.

const person = {
  firstName: 'Genesis',
  lastName: 'Gabiola',
  get fullName() {
    return `${person.firstName} ${person.lastName}`;
  },
  set fullName(value) {
    const parts = value.split(' ');
    this.firstName = parts[0];
    this.lastName = parts[1];
  }
};

person.fullName = 'John Smith';

console.log(person);

Try and Catch

We assumed that value is a valid string, but if we pass a boolen we get an error because split() is a method belongs to strings. Boolean doesn't have a split() method. But if we pass null or undefined we get a different error, for the same reason, we cannot call the split() on a null

const person = {
  firstName: 'Genesis',
  lastName: 'Gabiola',
  get fullName() {
    return `${person.firstName} ${person.lastName}`;
  },
  set fullName(value) {
    const parts = value.split(' ');
    this.firstName = parts[0];
    this.lastName = parts[1];
  }
};

person.fullName = true; // Uncaught TypeError: value.split is not a function at Object.set fullName [as fullName]

person.fullName = null; // Uncaught TypeError: Cannot read property 'split' of null at Object.set fullName [as fullName]

console.log(person);

We need to add a error handling typically in situation like this, we should do error handling at the beginning of a function or a method, this is what we called Defensive programming.

We want to make sure that the values that are coming in are valid, they are in the right shape, so we can execute are logic.

set fullName(value) {
  if (typeof value !== 'string') return;

So the null we passed will not take effect.

But sometimes we want to report an error in our application thats where we need to throw an exception.

Throwing an exeption is a technical jargon in a lot of programming languages.

Error is Constructor function because here we have Pascal case, calling it using the new operator to create a new error object.

throw new Error('Value is not a string.');

Some people confuse errors with exception, but there is a slight difference between the two.

const e = new Error();

Error object is just a plain javaScript object, there's nothing special about it, but the moment we throw this error we refer to that as exception.

const e = new Error();
throw e;

So this method is throwing an exception and somewhere else we need to catch that exception, and quiet often when we catch that exception we display an error to the user.

const parts = value.split(' ');
if (parts.length !== 2)
  throw new Error('Enter the first and last name');

try {
  // person.fullName = null;
  person.fullName = '';
}
catch (e) {
  alert(e);
}
const person = {
  firstName: 'Genesis',
  lastName: 'Gabiola',
  set fullName(value) {
    if (typeof value !== 'string')
      throw new Error('Value is not a string.');

    const parts = value.split(' ');
    if (parts.length !== 2)
      throw new Error('Enter the first and last name');

    this.firstName = parts[0];
    this.lastName = parts[1];
  }
};

try {
  person.fullName = null;
  // person.fullName = '';
}
catch (e) {
  alert(e);
}

console.log(person)

Local vs Global Scope

This is all about scoping. A scope of a variable or a constant determines where the variable or constant is accessible.

When we declare a variable with let and const their scope is limited to the block in which they are defined.

{
  const message = 'hi';
  console.log(message); // hi
}
console.log(message); // Uncaught ReferenceError: message is not defined
for (let i = 0; i < 5; i++) {
  console.log(i); // 0 | 1 | 2 | 3 | 4
}
  console.log(i); // Uncaught ReferenceError: i is not defined

We can have two same name variables in two different function.

const color = 'red';
function start() {
  const message = 'hi';
  console.log(color); // red
}

function stop() {
  const message = 'bye';
}

const color has a global scope. Global means its accessible everywhere globally, so it's accessible in all of the functions we have in the program.

Local variables or constants in a function take precedence over global variables or constants.

const color = 'red';
function start() {
  const color = 'blue';
  console.log(color); // blue
}

In general, it should avoid to declare global variables or constants, that is considered bad practice. And it's because they're accessible everywhere globally, and each function can accidentally changed their value, this will lead to all kinds of bugs and issues in the program.

Methaphor
Imagine variable is a toothbrush and each function is a person. You don't want a toothbrush that is shared with multiple people. Each person should have their own toothbrush.

Let vs Var

It has still the same concept even when removing the curly braces block.

let:

function start() {
  for (let i = 0; i < 5; i++) {
    console.log(i); // 0 | 1 | 2 | 3 | 4
  }
  console.log(i); // Uncaught ReferenceError: i not defined
}
start();

var:

function start() {
  for (var i = 0; i < 5; i++) {
    console.log(i); // 0 | 1 | 2 | 3 | 4
  }
  console.log(i); // 0 | 1 | 2 | 3 | 4 | 5
}
start();

It turns out that i is accessible outside the for block.

This is the issue with the var keyword. When you declare a variable with var its scope is not limited to the block in which its defined. Its limited to the function in which its defined.

Before ES6 var is the only way to declare a variables and constants, starting from ES6 (ES2015) now we have two new keywords let and const to define variables and constants, these two keywords create block-scoped variables, but var creates function-scoped variables.

function start() {
  if (true) {
    var color = 'red';
  }
  console.log(color); // red
}

Technically in most programming languages, color variable should only accessible in the block where its defined, but var is used to defined it, color variable is accessible anywhere in the function.

function start() {
  if (true) {
    let color = 'red';
  }
  console.log(color); // Uncaught ReferenceError: color is not defined
}

Replaced var with let, we get an error, because color variable is not accessible outside the block, and that's how most languages work.

That's the first problem with var keyword, the second is with global variables.

When var is used outside of a function this creates a global variable and attaches that global variable to the window object in the browser.

var color = 'red';
let age = 30;

window.color // "red"
window.age // undefined

It is bad to attach a variable to the window object, because window object central is something central there's only one instance of the window objec.

Let's say you're using a third party library, if that third party library also has a variable with the same name, that variable can overwrite your variable, so it should avoid adding stuff to the window object, otherwise you're going to create all sorts of issues in your program.

The "this" Keyword

this references the object that is executing the current function.

  1. Fist Rule: If the function is part of an object we call that function a method. If that function is a method in an object, this references that object itself.

  2. Second Rule: Otherwise if that function is just a regular function which means its not part of an object, this references to the global object, which is the window object in the browsers and global in node.

First Rule:

const video = {
  title: 'a',
  play() {
    console.log(this);
  }
};

video.stop = function() {
  console.log(this);
};

video.stop(); // {title: "a", play: f, stop: f}

play() is a method in the video object, this references the object itself, we added another method which is stop() and got the same result.

Second Rule:

// ...
function playVideo() {
  console.log(this);
};

playVideo(); // Window {postMessage: f, blur: f, focus: f, close: f, frames: Window, ...}

this in here has the global object which is window in browsers and global in node.

Using a Constructor Function, new operator

// ...
function Video(title) {
  this.title = title;
  console.log(this);
};

const v = new Video('b'); // Video {title: "b"}

In here, instead of a window object we got a new object which is the Video object, when you use the new operator, this new operator creates a new empty object like this {} and set this point to the same object.

Another example:

const video = {
  title: 'a',
  tags: ['a', 'b', 'c'],
  showTags() {
    this.tags.forEach(function(tag) {
      // console.log(tag); // a | b | c
      console.log(this.title, tag) // undefined "a" || undefined "b"|| undefined "c"
    });
  }
};
video.showTags();

this in here is referencing the window object, because the function in here is in a call back function and this function is just a regular function. It's not a method in the video object. The only method here is showTags().

To fix it and show the title of the video next to each tag.

forEach has two parameters is call back function, second parameter is thisArg, so we can pass an object here and this will reference that object.

// ...
      console.log(this.title, tag)
    }, this); // a a || a b || a c
  }
};
video.showTags();

But not all the methods in javaScript give you the ability to pass the this argument.

Changing the Value of "this"

Here are the few different solutions to change the value of this in a function.

Imagine forEach method doesn't have the second parameter.

  1. First Approach
    Before calling the forEach method we can define a const self or some people call this that, it's one of personal preference and set it to this.
    Now self is referencing the video object. So in our call back function we can use self instead of this, because the value of this changes when a new function is called.
const video = {
  title: 'a',
  tags: ['a', 'b', 'c'],
  showTags() {
    const self  = this;
    this.tags.forEach(function(tag) {
      console.log(self.title, tag)
    }, this); // a a || a b || a c
  }
};
video.showTags();

This is not the preferred approach, but it's something that you can see in a lot of javaScript applications out there.
Understand it but don't use this approach.

  1. Second Approach
    Functions are objects in javaScript, playVideo() is technically a object, so it has properties and methods that we can access using the dot notation.

We have three methods, apply(), bind() and call(), with these methods we can change the value of this for this function.

The Simplest is call() method, the first parameter of this method is thisArg, so here we can pass an object and this will reference that object.

call()

// ...
function playVideo() {
  console.log(this);
}
playVideo.call({ name: 'Genesis'}); // {name: "Genesis"}

In contrast when we call this function using the standard function call syntax, this references to this objects.

// ...
playVideo.call(); // Window object

apply()

// ...
playVideo.apply({ name: 'Genesis'}); // {name: "Genesis"}

We get the same result, what we pass as the first argument, will be use as the value of this.

The difference between call() and apply is only about passing argument.

// call()
playVideo.call({ name: 'Genesis'}, 1, 2);

// apply()
playVideo.apply({ name: 'Genesis'}, [1, 2 ]);

// bind()
playVideo.bind({ name: 'Genesis'});

bind() method doest not call the playVideo() function, it returns a new function and sets this to point to the object that we passed permanently. So no matter how we call that function this will always point to the object that we pass.

So this returns a new function and store the result in a const and call the function just like a regular function.

Going back to the previous example:

// ...
  showTags() {
    this.tags.forEach(function(tag) {
      console.log(this.title, tag)
    }.bind(this)); // a a || a b || a c
  }
};
video.showTags();

bind method is the second solution to solve this problem, but thereis a new and better solution.

Starting ES6, we have arrow functions, the good thing about this is that inherit the this value.

So removing the call to the bind() method and change the function to arrow function.

// ...
  showTags() {
    this.tags.forEach(tag => {
      console.log(this.title, tag)
    }); // a a || a b || a c
  }
};

video.showTags();

With this we get the same result without doing anything extra. The reason for this is arrow functions inherit this from the containing function. So in the showTags() function this reference to the video object, so when we pass an arrow function to the forEach the same this value is used in the arrow function. In other words, this is not rebound to a new object.

Exercise 1: Sum of Arguments

Create a function called sum() that takes a varying number of arguments and returns their sum. And then to challenge yourself modify the sum() function to be able to accept an array and still return the same result.

Use Array.isArray() method to detect if it's array.

console.log(sum([1, 2, 3, 4]));

function sum(...items) {
 if (items.length === 1 && Array.isArray(items[0]))
   items = [...items[0]];
 return items.reduce((a, b) => a + b);
}

Exercise 2: Area of Circle

Create a circle object using the Object Literal syntax, this object should have a reduce property that we can read or write to. We should also have an area property that is read-only, we should not be able to set the area in the outside, but should be able to read it.

const circle = {
  radius: 1,
  get area() {
    return Math.PI * this.radius * this.radius;
  }
}

console.log(circle.area);

Exercise 3: Error Handling

This function has a tiny problem, here we are assuming that the first argument is a valid an array, but if we call this function with a boolean, null, undefined or number we get an error.

We need to modify this function and add error handling.

const numbers = [1, 2, 3, 4];
const count = countOccurences(numbers, 1); // 1
console.log(count);

function countOccurences(array, searchElement) {
  return array.reduce((accumulator, current) => {
    const occurence = (current === searchElement) ? 1 : 0;
    return accumulator + occurence;
  }, 0);
}

If the first argument is not an array, throw an exception then wrap these 2 lines const count and console.log with a try catch block. Catch the exception and log it in the console.

try {
  const numbers = [1, 2, 3, 4];
  const count = countOccurences(numbers, 1); // 1
  console.log(count);
}
catch(e) {
  console.error(e.message);
}

function countOccurences(array, searchElement) {
  if (!Array.isArray(array))
    throw new Error('Invalid array!');
  return array.reduce((accumulator, current) => {
    const occurence = (current === searchElement) ? 1 : 0;
    return accumulator + occurence;
  }, 0);
}