the complete javascript course zero to expert

fundamentas 01

alert("Hello World!")

1
2
3
4
let js = 'amazing';
if (js === 'amazing') alert('Javascript is FUN!');
js = 'boring';
if (js === 'amazing') alert('Javascript is FUN!');

javascript is a high-level object-oriented, multi-paradigm programming language

programming language: instruct computer to do things
high level: we dont have to worry about complex stuff like memory management
object oriented: based on objects, for storing most kinds of data
multi paradigm: we can use different styles of programming

  • dynamic effects and web applications in the browser -> react agular vue
  • web applications on web servers -> nodejs
  • native mobile applications -> native react, ionic
  • native desktop applications -> electorn

ES6/ES2015

1
2
40 + 8 + 23 - 10;
console.log(40 + 8 + 23 - 10);

value and variables

1
2
3
4
5
6
7
console.log('Jonas');
console.log(23);

let firstName = 'Matilda';
console.log(firstName);
console.log(firstName);
console.log(firstName);

camlcase
let first = 'Jonas';
let firstNamePerson;

error number first
let 2ads = 2;

letter
number8
undersocre_
dollarsign$

no key word

constants
PI

class name
Person

name descriptive
let myFirstJob = 'Programmer';
let myCurrentJob = 'Teacher';
not
let job1 = 'Programmer';
let job2 = 'Teacher';

data types

dymatic type

object or primitive

primitive

number
floating point used for dicimals and integers

string
sequence of characters

boolean
true or false

undifined
value taken by a variable that is not yet defined(‘empty value’)
let children;

null

symbol(es2015)
unique and cannot be changed

bigint(es2020)
larger integers

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

// console.log(true);
let javascriptIsFun = true;
console.log(javascriptIsFun);

// console.log(typeof true); // 'boolean'
console.log(typeof javascriptIsFun); // 'boolean'
// console.log(typeof 23); // 'number'
// console.log(typeof 'Jonas'); // 'string'
// console.log(typeof "Jonas"); // 'string'

javascriptIsFun = 'YES!';
console.log(typeof javascriptIsFun); // 'string'

let year;
console.log(year); // undefined
console.log(typeof year); // 'undefined'

year = 1991;
console.log(year); // 1991
console.log(typeof year); // 'number'

// javascript bug
console.log(typeof null); // 'object'

let const var

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let age = 30;
age = 31;

const birthYear = 1991;
//script.js:51 Uncaught TypeError: Assignment to constant variable.
// birthYear = 1990;
//Uncaught SyntaxError: Missing initializer in const declaration
// const job;

// let is block scope var is functiong scope
var job = 'programmer';
job = 'teacher';

// not recomment, global scope
lastName = 'asdf';
console.log(lastName);

basic operators

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
const now = 2037
// const ageJonas = 2037 - 1991;
const ageJonas = now - 1991;
// const ageSarah = 2037 - 2018;
const ageSarah = now - 2018;
console.log(ageJonas);
console.log(ageJonas, ageSarah); // 46 19

console.log(ageJonas * 2, ageJonas / 10, 2 ** 3);
//2**3 = 2 * 2 * 2

const firstName = 'Jonas';
const lastName = 'Schemd';
console.log(firstName + ' ' + lastName); // Jonas Schemd

//asignment
let x = 10 + 5; // 15
x += 10; // x = x + 10 = 25
x *= 4 // x = x * 4 = 100
x++; // x = x + 1
x--;
console.log(x); //

// comparison
console.log(ageJonas > ageSarah); // true
console.log(ageJonas >= 18); // true

const isFullAge = ageJonas >= 18;
console.log(isFullAge);


console.log(now - 1991 > now - 2018);

percedence

mdn operator precedence

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const now = 2037;
const ageJonas = now - 1991;
const ageSarah = now - 2018;

console.log(now - 1991 > now - 2018);

let x, y;
x = y = 25 - 10 - 5;
console.log(x, y); // 10 10

// const averageAge = ageJonas + ageSarah / 2 // 55.5
const averageAge = (ageJonas + ageSarah) / 2 // 32.5
console.log(ageJonas, ageSarah);
console.log(averageAge);

Coding Challenge #1

string and template literal

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
const firstName = 'Jonas'
const job = 'teacher';
const birthYear = 1991;
const year = 2037;

// I'm Jonas, a 46 years old teacher!
const jonas = "I'm " + firstName + ', a ' + (year - birthYear) + ' years old ' + job + '!';
console.log(jonas);

//es6
const jonasNew = `I'm ${firstName}, a ${year - birthYear} years old ${job}!`;
console.log(jonasNew);

console.log(`Just a regular string...`);

//multiline string
// String with
// multiple
// lines
console.log('String with \n\
multiple\n\
lines');

console.log(`String
multiple
line`);

if else control structure

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
const age = 15;
// const age = 19;
// const isOldEnough = age >= 18;

// if (isOldEnough) {
// console.log('Sarah can start driving licese!');
// }


if (age >= 18) {
console.log('Sarah can start driving licese!');
} else {
const yearsLeft = 18 - age;
//Sarah is too young. Wait another 3 years :)
console.log(`Sarah is too young. Wait another ${yearsLeft} years :)`);
}

// const birthYear = 1991;
const birthYear = 2012;
let century;
if (birthYear <= 2000) {
// let century = 20;
century = 20;
} else {
century = 21;
}

// Uncaught ReferenceError: century is not defined
// console.log(century);

console.log(century);

type conversion and coercion

coercion auto
conversion manually

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

// type coversion
const inputYear = '1991';
console.log(inputYear + 18); // '199118'

console.log(Number(inputYear), inputYear); // 1991 '1991'
console.log(Number(inputYear) + 18); // 2009

// NaN not a valid number
console.log(Number('Jonas')); // NaN
console.log(typeof NaN); // 'number'

console.log(String(23), 23); // '23' 23

// type coercion
// 'I am 23 years old'
console.log('I am ' + 23 + ' years old');
// console.log('I am ' + String(23) + ' years old');

console.log('23' - '10' - 3); // 10
// console.log(Number('23') - Number('10') - 3); // 10
console.log('10' * '2'); // 20
console.log('10' / '2'); // 5


let n = '1' + 1; // 11
n = n - 1;
console.log(n); // 10

console.log(2 + 3 + 4 + '5');

truthy and falsy values

boolean coercion

  • logical operator
  • logical context: if else statement
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 5 falsy values: 0, '', undefined, null, NaN
// else are truthy values

console.log(Boolean(0)); // false
console.log(Boolean(undefined)); // false
console.log(Boolean('')); // false
console.log(Boolean('Jonas')); // true
console.log(Boolean({})); // true
console.log(Boolean(-1)); // true



// const money = 0; // you should get a job
const money = 100; // dont spent it all
if (money) {
console.log('dont spent it all');
} else {
console.log('you should get a job');
}

let height; // height is undefined
// let height = 0; // height is undefined
// let height = 100; // height is defined
if (height) {
console.log('height is defined');
} else {
console.log('height is undefined');
}

equal operators

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
const age = 18;

// no type coercion
if (age === 18) console.log('You just became an adult!');

// true
if ('18' == 18) console.log('You just became an adult!');
// if (Number('18') == 18) console.log('You just became an adult!');

// false
if ('18' === 18) console.log('You just became an adult!');

// input: 23
let favourite = prompt("What's your favorite number?");
console.log(favourite); // '21'
console.log(typeof favourite); // 'string'

if (favourite == 23) {
console.log('23 is an amazing number!');
}

if (Number(favourite) === 23) {
console.log('23 is an amazing number!');
}

favourite = Number(favourite);
if (favourite === 23) {
console.log('23 is an amazing number!');
} else if (favourite === 7) {
console.log('7 is an amazing number!');
} else {
console.log('number is not 7 or 23!');
}

if (favourite !== 23) console.log('why not 23!');

boolean logic operators

and or not

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

// const hasDriverLicense = true;
// const hasGoodVision = true;

// console.log(hasDriverLicense && hasGoodVision); // true

const hasDriverLicense = true;
const hasGoodVision = false;

console.log(hasDriverLicense && hasGoodVision); // false
console.log(hasDriverLicense || hasGoodVision); // ture
console.log(!hasDriverLicense); // false


// const shouldDrive = hasDriverLicense && hasGoodVision;

if (hasDriverLicense && hasGoodVision) {
console.log('sarah is able to drive!');
} else {
console.log('someone else should drive...');
}


const isTired = true;
console.log(hasDriverLicense && hasGoodVision && isTired);

if (hasDriverLicense && hasGoodVision && !isTired) {
console.log('sarah is able to drive!');
} else {
console.log('someone else should drive...');
}

switch statement

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

const day = 'thursday';

switch (day) {
case 'monday': // day === 'monday'
console.log('plan course structure');
break;
case 'tuesday':
console.log('prepare theory videos');
break;
case 'wednesday':
case 'thursday':
console.log('write code examples');
break;
case 'friday':
console.log('record videos');
break;
case 'saturday':
case 'sunday':
console.log('enjoy the weekend');
break;
default:
console.log('not a valid day');
break;
}

if (day === 'monday') {
console.log('plan course structure');
} else if (day === 'tuesday') {
console.log('prepare theory videos');
} else if (day === 'wednesday' || day === 'thursday') {
console.log('write code examples');
} else if (day === 'friday') {
console.log('record videos');
} else if (day === 'saturday' || day === 'sunday') {
console.log('enjoy the weekend');
} else {
console.log('not a valid day');
}

statement and expression

expression produce a value

3 + 4
1991
true && false && !false

statement
not produce a value
if else statement

// error
console.log(i am ${2037 - 1991} years old ${if(true){console.log('hello');}});

condition operator (ternary)

it is an expression
only one line block of code

es5 es6+

use babel to transpile and polyfill code
coverting back to es5 to ensure browser compatibility for all users

fundamentas 02

strict mode

first line
'use strict';

1
2
3
4
5
6
7
8
9
'use strict';

let hasDriversLicense = false;
const passTest = true;


//Uncaught ReferenceError: hasDriverLicense is not defined
if (passTest) hasDriverLicense = true;
if (hasDriversLicense) console.log('i can dirve');

function

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function logger() {
console.log('My name is Jonas');
}

// calling / running / invoking function
logger(); // return undefined
logger();
logger();

// parameters
function fruitProcessor(apples, oranges) {
// console.log(apples, oranges);
const juice = `Juice with ${apples} apples and ${oranges} oranges.`;
return juice;
}

//arguments
const appleJuice = fruitProcessor(5, 0); // Juice with 5 apples and 0 oranges.
console.log(appleJuice);
// 5 3
// Juice with 5 apples and 3 oranges.
console.log(fruitProcessor(5, 3));

const appleOrangeJuice = fruitProcessor(2, 4);
console.log(appleOrangeJuice);

function declarations vs experssions

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// function declaration
const age1 = calcAge1(1991);
function calcAge1(birthYeah) {
// const age = 2037 - birthYeah;
// return age;
return 2037 - birthYeah;
}

console.log(age1);


// Uncaught ReferenceError: Cannot access 'calcAge2' before initialization
// const age2 = calcAge2(1991);

// function expression
const calcAge2 = function (birthYeah) {
return 2037 - birthYeah;
}

const age2 = calcAge2(1991);
console.log(age2);

arrow functions

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

const calcAge2 = function (birthYeah) {
return 2037 - birthYeah;
}

// arrow function
// one param
// one return expression
const calcAge3 = birthYeah => 2037 - birthYeah;
const age3 = calcAge3(1991);
console.log(age3);

const yearUntilRetirement = birthYeah => {
const age = 2037 - birthYeah;
const retirement = 65 - age;
return retirement;
}
console.log(yearUntilRetirement(1991)); //19

const yearUntilRetirement1 = (birthYeah, firstName) => {
const age = 2037 - birthYeah;
const retirement = 65 - age;
return `${firstName} retires in ${retirement}`;
}
// Jonas retires in 19
console.log(yearUntilRetirement1(1991, 'Jonas'));
// Bob retires in 8
console.log(yearUntilRetirement1(1980, 'Bob'));

functions calling other functions

alt text

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

function cutFruitPieces(fruit) {
return fruit * 4
}

function fruitProcessor(apples, oranges) {
// console.log(apples, oranges);
const applePieces = cutFruitPieces(apples);
const orangePieces = cutFruitPieces(oranges);
const juice = `Juice with ${applePieces} pieces of apples and ${orangePieces} pieces of oranges.`;
return juice;
}

//Juice with 8 pieces of apples and 12 pieces of oranges.
console.log(fruitProcessor(2, 3));

function reviewing

alt text

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

const calcAge = function (birthYeah) {
return 2037 - birthYeah;
}

const yearUntilRetirement = function (birthYeah, firstName) {
const age = calcAge(birthYeah);
const retirement = 65 - age;
if (retirement > 0) {
return retirement;
// return statement immediatly finsih the function
console.log(`${firstName} retires in ${retirement}`);
} else {
return -1;
}


}

console.log(yearUntilRetirement(1991, 'Jonas'));
console.log(yearUntilRetirement(1970, 'Mike'));

introducing to array data structure

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

// const friend1 = 'Michael';
// const friend2 = 'Steven';
// const friend3 = 'Peter';

const friends = ['Michael', 'Steven', 'Peter'];
console.log(friends);

const years1 = new Array(1991, 1984, 2008, 2020);
console.log(years1);

console.log(friends[0]); // Michael
console.log(friends[2]); // Peter

console.log(friends.length); // 3
console.log(friends[friends.length - 1]); // Peter

friends[2] = 'Jay';
console.log(friends); // ['Michael', 'Steven', 'Jay']

// script.js:201 Uncaught TypeError: Assignment to constant variable.
// friends = ['Bob', 'Alice'];

const firstName = 'Jonas';
const jonas = [firstName, 'Schmed', 2037 - 1991, 'teacher', friends];
console.log(jonas); // ['Jonas', 'Schmed', 46, 'teacher', Array(3)]

//exercise

const calcAge = function (birthYeah) {
return 2037 - birthYeah;
}
const years = [1990, 1967, 2002, 2010, 2018];

const age1 = calcAge(years[0]);
console.log(age1);
console.log(calcAge(years[0]));


// [47, 70, 19]
const ages = [calcAge(years[0]), calcAge(years[1]), calcAge(years[years.length - 1])];
console.log(ages);

basic array operations(methods)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

const friends = ['Michael', 'Steven', 'Peter'];

//add elements

const newLength = friends.push('Jay');
// ['Michael', 'Steven', 'Peter', 'Jay'] 4
console.log(friends, newLength);

friends.unshift("John");
// ['John', 'Michael', 'Steven', 'Peter', 'Jay']
console.log(friends);

// remove elements

const lastFriend = friends.pop();
//['John', 'Michael', 'Steven', 'Peter'] 'Jay'
console.log(friends, lastFriend);
friends.pop();
// ['John', 'Michael', 'Steven']
console.log(friends);

const firstFriend = friends.shift();
//  ['Michael', 'Steven', 'Peter'] 'John'
console.log(friends, firstFriend);

console.log(friends.indexOf('Steven')); // 1
console.log(friends.indexOf('Bob')); // -1

// friends.push(23);
console.log(friends.includes('Steven')); // true
// === equal
// console.log(friends.includes("23")); // false

if (friends.includes('Steven')) {
console.log('you have a friend called Steven');
}

introduce to Objects

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

const jonas = {
firstName: 'Jonas',
lastName: 'Schmed',
age: 2037 - 1991,
job: 'teacher',
friends: ['Michael', 'Peter', 'Steven']
}

// order is not important not matter

// dot and bracket notation

console.log(jonas.lastName); // "Schmed"

// bracket can be any experssion
console.log(jonas["lastName"]); // "Schmed"
// bracket can be any experssion
const nameKey = 'Name';
console.log(jonas[`first${nameKey}`]); // Jonas
console.log(jonas['last' + nameKey]); // "Schmed"


//input 'job'
const interestedIn = 'job';
// const interestedIn = prompt('What do you want to know about Jonas? Choose from firstName, lastName, age, job, and friends');
console.log(interestedIn);
console.log(jonas[interestedIn]);
// console.log(jonas.interestedIn); // jonas.interestedIn ===> undefinded

if (jonas[interestedIn]) {
console.log(jonas[interestedIn]);
} else {
console.log('wrong request');
}

// add new property

jonas.location = 'Portugal';
jonas['twitter'] = '@jonas';
console.log(jonas);

//Jonas has 3 friends, and his best friend is called Michael
console.log(`${jonas.firstName} has ${jonas.friends.length} friends, and his best friend is called ${jonas.friends[0]}`);

object methods

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90

const jonas = {
firstName: 'Jonas',
lastName: 'Schmed',
birthYear: 1991,
job: 'teacher',
friends: ['Michael', 'Peter', 'Steven'],
hasDriverLicense: true,

calcAge: function (birthYear) {
return 2037 - birthYear;
}

//error
// function func1(params) {
//
// }
};

// 46
console.log(jonas.calcAge(1991));
console.log(jonas['calcAge'](1991));

// this key word

const jonas1 = {
firstName: 'Jonas',
lastName: 'Schmed',
birthYear: 1991,
job: 'teacher',
friends: ['Michael', 'Peter', 'Steven'],
hasDriverLicense: true,

calcAge: function () {
// {firstName: 'Jonas', lastName: 'Schmed', birthYear: 1991, job: 'teacher', friends: Array(3), …}
// console.log(this);
return 2037 - this.birthYear;
// not recomment
// return 2037 - jonas1.birthYear;

}

};

console.log(jonas1.calcAge());



const jonas2 = {
firstName: 'Jonas',
lastName: 'Schmed',
birthYear: 1991,
job: 'teacher',
friends: ['Michael', 'Peter', 'Steven'],
hasDriverLicense: true,

calcAge: function () {
this.age = 2037 - this.birthYear;
return this.age;
}

};

console.log(jonas2.calcAge());
console.log(jonas2.age);
console.log(jonas2.age);

// challenge
const jonas3 = {
firstName: 'Jonas',
lastName: 'Schmed',
birthYear: 1991,
job: 'teacher',
friends: ['Michael', 'Peter', 'Steven'],
hasDriverLicense: true,

// selfIntro: function () {
// return `${this.firstName} is a ${2037 - this.birthYear}-year old ${this.job}, and he has ${this.hasDriverLicense ? 'a' : 'no'} driver's license`;
// }
calcAge: function () {
this.age = 2037 - this.birthYear;
return this.age;
},
selfIntro: function () {
return `${this.firstName} is a ${this.calcAge()}-year old ${this.job}, and he has ${this.hasDriverLicense ? 'a' : 'no'} driver's license.`;
}

};
// Jonas is a 46-year old teacher, and he has a driver's license
console.log(jonas3.selfIntro());

iteration for loop

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

// console.log('lifting weights repetition 1');
// console.log('lifting weights repetition 2');
// console.log('lifting weights repetition 3');
// console.log('lifting weights repetition 4');
// console.log('lifting weights repetition 5');
// console.log('lifting weights repetition 6');

// lifting weights repetition 1
// lifting weights repetition 2
// lifting weights repetition 3
// lifting weights repetition 4
// lifting weights repetition 5

// condition evaluated before each iteration
// counter update after each iteration
for (let rep = 1; rep <= 5; rep++) {
console.log(`lifting weights repetition ${rep}`);
}

loop array, break, continue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
const jonas = [
'Jonas',
'Schmed',
2037 - 1991,
'teacher',
['Michael', 'Peter', 'Steven']
];


//i = 4 ['Michael', 'Peter', 'Steven'] 'object'
for (let i = 0; i < jonas.length; i++) {
console.log(jonas[i], typeof jonas[i]);
}

const types = [];
for (let i = 0; i < jonas.length; i++) {
// types[i] = typeof jonas[i];
types.push(typeof jonas[i]);
}
// ['string', 'string', 'number', 'string', 'object']
console.log(types);


const years = [1991, 2007, 1969, 2020];
const ages = [];

for (let i = 0; i < years.length; i++) {
ages.push(2037 - years[i]);
}
//[46, 30, 68, 17]
console.log(ages);


// continue and break
// continue: skip this iteration
// break: finish the whole loop

console.log('-------all items-------------');
for (let i = 0; i < jonas.length; i++) {
console.log(jonas[i], typeof jonas[i]);
}

console.log('-------only strings-------------');
for (let i = 0; i < jonas.length; i++) {
if (typeof jonas[i] !== 'string') {
continue;
}
console.log(jonas[i], typeof jonas[i]);
}

console.log('-------break with number-------------');
for (let i = 0; i < jonas.length; i++) {
if (typeof jonas[i] === 'number') {
break;
}
console.log(jonas[i], typeof jonas[i]);
}

looping backwards and loops in loops

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

const jonas = [
'Jonas',
'Schmed',
2037 - 1991,
'teacher',
['Michael', 'Peter', 'Steven']
];

// loop backwards
for (let i = jonas.length - 1; i >= 0; i--) {
console.log(i, jonas[i]);
}

// loop in loop

// ---------starting exercise 1------------
// Exercise 1 Lifting weight repetition 1
// Exercise 1 Lifting weight repetition 2
// Exercise 1 Lifting weight repetition 3
// Exercise 1 Lifting weight repetition 4
// Exercise 1 Lifting weight repetition 5
// ---------starting exercise 2------------
// Exercise 2 Lifting weight repetition 1
// Exercise 2 Lifting weight repetition 2
// Exercise 2 Lifting weight repetition 3
// Exercise 2 Lifting weight repetition 4
// Exercise 2 Lifting weight repetition 5
// ---------starting exercise 3------------
// Exercise 3 Lifting weight repetition 1
// Exercise 3 Lifting weight repetition 2
// Exercise 3 Lifting weight repetition 3
// Exercise 3 Lifting weight repetition 4
// Exercise 3 Lifting weight repetition 5

for (let exercise = 1; exercise < 4; exercise++) {
console.log(`---------starting exercise ${exercise}------------`);
for (let rep = 1; rep < 6; rep++) {
console.log(`Exercise ${exercise} Lifting weight repetition ${rep}`);
}
}

while loop

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

for (let rep = 1; rep <= 5; rep++) {
console.log(`lifting weights repetition ${rep}`);
}

console.log('----------------');


// while loop


let rep = 1;
while (rep <= 5) {
console.log(`lifting weights repetition ${rep}`);

rep++;
}

let dice = Math.trunc(Math.random() * 6) + 1;
// console.log(dice);

while (dice !== 6) {

console.log(`You rolled a ${dice}`);
dice = Math.trunc(Math.random() * 6) + 1;
if (dice === 6) {
console.log('dice is 6, loop is about to end...');
}
}

developer skills

DOM AND EVENT – guess my number

console.log(document.querySelector('.message')); // element p.message

console.log(document.querySelector('.message').textContent); // "Start guessing..."

select and manupulating elements

document.querySelector('.message').textContent = 'Correct Number!'

document.querySelector('.guess').value = 10;

handling event -> event lisener

(method) Element.addEventListener<keyof ElementEventMap>(type: keyof ElementEventMap, listener: (this: Element, ev: Event) => any, options?: boolean | AddEventListenerOptions | undefined): void (+1 overload)

1
2
3
4
5
6
7
8
9
10
11
12
// addEventListener -> most used one
document.querySelector('.check').addEventListener('click', function () {
// console.log(document.querySelector('.guess').value); // ""
// const guess = document.querySelector('.guess').value;
// console.log(typeof guess); // 'string'
// "" --> 0
const guess = Number(document.querySelector('.guess').value);

if (!guess) {
document.querySelector('.message').textContent = 'No number'
}
});

implementing the game logic

manipulation css styles

change the html inline css style

// manupilating css style
document.querySelector('body').style.backgroundColor = '#60b347';

document.querySelector('.number').style.width = '30rem';

the dry principle

project 2 modal

1
2
const btnsShowModal = document.querySelectorAll('.show-modal');
console.log(btnsShowModal); // NodeList(3) like array

css class manupulating

const modal = document.querySelector('.modal');
modal.classList.remove("hidden", 'class-name');

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// btnCloseModal.addEventListener('click', () => {
// modal.classList.add("hidden");
// overlay.classList.add("hidden");
// });


// overlay.addEventListener('click', () => {
// modal.classList.add("hidden");
// overlay.classList.add("hidden");
// });

const closeModal = function () {
modal.classList.add("hidden");
overlay.classList.add("hidden");
}

btnCloseModal.addEventListener('click', closeModal);
overlay.addEventListener('click', closeModal);

key press event

global event
document

1
2
3
4
5
6
7
8
9
10
document.addEventListener('keydown', function (e) {

// a key was pressed
// KeyboardEvent {isTrusted: true, key: 'Escape', code: 'Escape', location: 0, ctrlKey: false, …}
// Escape
// console.log('a key was pressed', e, e.key);
if (e.key === 'Escape' && !modal.classList.contains('hidden')) {
closeModal();
}
});

project 3 pig game

alt text

const player1Score = document.getElementById('score--0');

// set img element attribute src
dice.src = `dice-${String(currentDice)}.png`;

document.querySelector(`.player--${currentPlayer}`).classList.toggle("player--active");

how javascript works

high level overview of javascript

javascript is a high level, prototype-based object-oriented, multi-paradigm, interpreted or just-in-time compied, dynamic, single-threaded, garbage-collected programming language with first-class functions and a non-blocking event loop concurrency model.

high level
manage computer resources automatically
garbage-collected

interpreted or just in time compiled
javascirpted engine

multi-paradigm
paradigm: an approach and mindset of structuring code, which will direct your coding style and technique.

  • procedural
  • objected oriented
  • functional

prototype-based object oriented

first-class functions
functions are simply treated as variables. we can pass them into other functions, return them from functions.

dynamic type
no data type when variable definitons.
data type of variable is automatically changed

single-threaded
non-blocking event loop

concurrency model: the javascript engine handles multiple tasks happpening at the same time.
event loop: takes long running tasks, executes them in the background, and put them back in the main thread once they are finished.

javascript engine and runtime

program that executes javascript code.

v8 engine
chrome, nodejs

call stack
heap

alt text

compilation vs interpretation
compilation: entire code is coverted into machine code at once, and written to a binary file that can be executed by a computer.
interpretation: interpreter runs through the source code and executes it line by line.

just-in-time comilation
entire code is converted into machine code at once, then executed immediately.

parse into AST:
abstract signtax tree

re-compilate optimization

js runtime in the browser

js runtime in the browser

nodejs runtime

alt text

execution contexts and the call stack

exeution context
environment in which a piece of javascript is executed. stores all the necessary information for some code to be executed.

one global execution context(ec)
default context, created from code that is not inside any function(top level)

function execution context(ec)
for each function call, a new execution context is created.

  • varialbe environment
    • let, const and var declarations
    • Functions
    • arguments object
  • scope chain: consists of references to varialbes that are lacated outside of the function
  • this keyword

!!! arrow functin dont have arguents object, this keyword
they used the arguemnt object and this keyword from the closest function pareant.

execution context

call stack:
place where execution contexts get stacked on top of each other, to keep track of where we are in the execution.

alt text

alt text

alt text

alt text

alt text

scope and scope chain

scoping: how programs’s variabes are organized and accessed.
“where do variables live?” or “where can we access a certain vairalbe, and where not?”

lexical scoping:
scoping is controlled by placement of functions and blocks in the code;
example like: a function write inside another function can have access to variable of the parent function

scope: space or environment in which a certain variable is declared(variable environment in case of functions scope). there is global scope, function scope, and block scope;

scope of a variable: region of our code where a certain variable can be accessed

3 types of scope

  • global scope
1
2
3
const me = 'Jonas';
const job = 'teacher';
const year = 1989;

outside of any function or block
variable declared in global scope are accessible everywhere

  • function scope
1
2
3
4
5
6
7
function calcAge(birthYear) {
const now = 2037;
const age = now - birthYear;
return age;
}

console.log(now) // ReferenceError

variables are accessible only inside function, Not outside
also called local scope

  • block scope (ES6)
1
2
3
4
5
6
7
// example: if block, for loop block, etc
if(year >= 1981 && year <= 1986) {
const millenial = true;
const food = 'Avocado toast';
}

console.log(millenial) // ReferenceError

variable are accessible only inside block(block scoped)
however, this only applies to let and const variables!
functions are also block scoped(only in strict mode)

scope chain

alt text

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const myName = "Jonas";

function first(){
const age = 30;

if (age >= 30) {
const decade = 3;
var millenial = true;
}

function second(){
const job = 'teacher';

console.log(`${myName} is a ${age}-old ${job}`);
// Jonas is a 30-old teacher
}

second();
}

first();

scope chain vs call stack

alt text

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

const a = 'Jonas';
first();

function first() {
const b = 'Hello!';
second();

function second() {
const c = 'Hi!';
third();
}
}

function third() {
const d = 'Hey!';
console.log(d + c + b + a);
// ReferenceError
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
function calcAge(birthYear) {
const age = 2037 - birthYear;
console.log(firstName); // no errors
// console.log(lastName); // ReferenceError

function printAge() {
let output = `${firstName} You are ${age}, born in ${birthYear}`;
console.log(output);

if (birthYear >= 1981 && birthYear <= 1996) {
var millenial = true;

// // scope chain shadow: create new variable with the same name as outer scope's variable
// const firstName = 'Steven'
// const str1 = `oh, you are a millenial, ${firstName}`;
// // oh, you are a millenial, Steven
// console.log(str1);

// // set the value again, reassigning outer scope's varialbe
// output = "New output!";

const str = `oh, you are a millenial, ${firstName}`;
//oh, you are a millenial, Jonas
console.log(str); // no error

function add(a, b) {
return a + b;
}
}
// // set the value again, reassigning outer scope's varialbe
// output = "New output!";
// // "New output!"
// console.log(output);


// Uncaught ReferenceError: str is not defined
// console.log(str);

// var vairale is function scoped
console.log(millenial); // no error "true"

// in strict mode
// function defined in block is block scoped
//script.js:80 Uncaught ReferenceError: add is not defined
// add(2, 3); // error
}
// Jonas You are 46, born in 1991
printAge(); // no error
return age;
}

const firstName = 'Jonas';
calcAge(1991);
// Uncaught ReferenceError: age is not defined
// console.log(age);
//Uncaught ReferenceError: printAge is not defined
// printAge();

variable environment: hoisting

hoisting:makes some types of variable acessible/usable in the code before they are actually declared.
“ variables lifted to the top of their scope”.
before execution, code is scanned for variable declarations, and for each variable, a new property is created in the variable environment object.

temporal dead zone

alt text

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const myName = "Jonas";

if (myName === 'Jonas') {
// Uncaught ReferenceError: Cannot access 'job' before initialization
// console.log(`Jonas is a ${job}`);

const age = 2037 - 1989;
console.log(age);
const job = 'teacher';

// Uncaught ReferenceError: x is not defined
// console.log(x);
}

  • make it easier to avoid and catch erros: accessing variables before declaration is ba d prctice and should be avoided;
  • makes const variables actully work
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// undefined
console.log(me);
// Uncaught ReferenceError: Cannot access 'job' before initialization
// console.log(job);
// script.js:137 Uncaught ReferenceError: Cannot access 'year' before initialization
// console.log(year);

var me = 'Jonas';
let job = 'teacher';
const year = 1991;

// functions

// no error
addDecl(2, 3);
// ReferenceError: Cannot access 'addExpr1' before initialization
// addExpr1(2, 3);
// ReferenceError: Cannot access 'addExpr2' before initialization
// addExpr2(2, 3);
// ===> undefined();
// Uncaught TypeError: addExpr3 is not a function
// addExpr3(2, 3);
// Uncaught ReferenceError: Cannot access 'addArrow' before initialization
// addArrow(2, 3);

function addDecl(a, b) {
return a + b;
}

const addExpr1 = function (a, b) {
return a + b;
};

let addExpr2 = function (a, b) {
return a + b;
};

var addExpr3 = function (a, b) {
return a + b;
};

const addArrow = (a, b) => a + b;
1
2
3
4
5
6
7
var x = 1;
let y = 2;
const z = 3;

console.log(x === window.x); // true
console.log(x === window.y); // false
console.log(x === window.z); // false

this keyword

this keyword/variable: special variable that is created for every execution context(every funtion). Takes the value of(point to) the “owner” of the function in which the this keyword is used.

this is NOT static.
it depends on how the function is called, and its valued is only assigned when the function is acturally called.

  • Method
    object that is calling the method
1
2
3
4
5
6
7
8
9
10
const jonas = {
name: 'Jonas',
year: 1989,
calcAge: function(){
return 2037 - this.year;
}
}

jonas.calcAge(); // 48

  • simple function call
    in strict mode
    undefinded

  • Arrow functions
    this of surrounding function (lexical this)

  • event listener
    DOM element that the handler is attatched to

  • new, call, apply, bind

this does not point to the function itself, and also NOT the its variable environment

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54

// Window {window: Window, self: Window, document: document, name: '', location: Location, …}
console.log(this);

const calcAge = function (birthYear) {
console.log(2037 - birthYear);
console.log(this);
}

// undefined
calcAge(1991);

const calcAgeArrow = (birthYear) => {
console.log(2037 - birthYear);
console.log(this);
}

// Window {window: Window, self: Window, document: document, name: '', location: Location, …}
calcAgeArrow(1991);

const jonas = {
year: 1991,
calcAge: function () {
console.log(this);
console.log(2037 - this.year);
}
};

// {year: 1991, calcAge: ƒ}
// 46
jonas.calcAge();

const matida = {
year: 2017,
};

matida.calcAge = jonas.calcAge;
// {year: 2017, calcAge: ƒ}
console.log(matida);


// {year: 2017, calcAge: ƒ}
// 20
matida.calcAge();

const f = jonas.calcAge;
// ƒ () {
// console.log(this);
// console.log(2037 - this.year);
// }
console.log(f);
// script.js:225 Uncaught TypeError: Cannot read properties of undefined
// f();

this in regular and arrow fucntions

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// dont use arrow function this time

const jonas = {
firstName: 'Jonas',
year: 1991,
calcAge: function () {
console.log(this);
console.log(2037 - this.year);
},

greet: () => console.log(`Hey ${this.firstName}`, this)
};

// "Hey undefined"
// Window {window: Window, self: Window, document: document, name: '', location: Location, …}
jonas.greet();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

// use arrow function this time

const jonas1 = {
firstName: 'Jonas',
year: 1991,
calcAge: function () {
console.log(this);
console.log(2037 - this.year);


// const isMillenial = function () {
// console.log(this);
// console.log(this.year >= 1981 && this.year <= 1996);
// }
// undefined
// Uncaught TypeError: Cannot read properties of undefined (reading 'year')
// isMillenial();

// solution 1
const self = this;
const isMillenial1 = function () {
console.log(self);
console.log(self.year >= 1981 && self.year <= 1996);
}
// {firstName: 'Jonas', year: 1991, calcAge: ƒ, greet: ƒ}
// true
isMillenial1();

// solution 2
const isMillenial2 = () => {
console.log(this);
console.log(this.year >= 1981 && this.year <= 1996);
}
// {firstName: 'Jonas', year: 1991, calcAge: ƒ, greet: ƒ}
// true
isMillenial2();

},

greet: () => console.log(`Hey ${this.firstName}`, this)
};

// "Hey undefined"
// Window {window: Window, self: Window, document: document, name: '', location: Location, …}
jonas1.greet();

jonas1.calcAge();

primitives vs object (primitive vs reference types)

alt text

alt text

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

let age = 30;
let oldAge = age;
age = 31;
console.log(age); // 31
console.log(oldAge); // 30

const me = {
name: 'Jonas',
age: 30,
};

const friend = me;
friend.age = 27;
// {name: 'Jonas', age: 27}
console.log('Me', me);
// {name: 'Jonas', age: 27}
console.log('Friend', friend);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54

///////////////// primitive type
let lastName = 'Williams';
let oldLastName = lastName;
lastName = "Davis";
console.log(lastName); // Davis
console.log(oldLastName); // Williams


///////////// reference type
const jessica = {
firstName: 'Jessica',
lastName: 'Williams',
age: 27,
};

let marriedJessica = jessica;
marriedJessica.lastName = 'Davis';
// Before marriage: {firstName: 'Jessica', lastName: 'Davis', age: 27}
console.log('Before marriage:', jessica);
// After marriage: {firstName: 'Jessica', lastName: 'Davis', age: 27}
console.log('After marriage:', marriedJessica);

// marriedJessica = {};

///////////// copying objects



const jessica2 = {
firstName: 'Jessica',
lastName: 'Williams',
age: 27,
family: ['Alice', 'Bob']
};

const jessicaCopy = Object.assign({}, jessica2);

jessicaCopy.lastName = 'Davis';

// Before marriage: {firstName: 'Jessica', lastName: 'Williams', age: 27}
console.log('Before marriage:', jessica2);
// After marriage: {firstName: 'Jessica', lastName: 'Davis', age: 27}
console.log('After marriage:', jessicaCopy);


/////////// shalow copy
jessicaCopy.family.push('Mary');
jessicaCopy.family.push('John');

// Before marriage: {firstName: 'Jessica', lastName: 'Williams', age: 27, family: Array(4)}
console.log('Before marriage:', jessica2);
// Before marriage: {firstName: 'Jessica', lastName: 'Williams', age: 27, family: Array(4)}
console.log('After marriage:', jessicaCopy);

data structures, strings

destructuring array

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

const arr = [2, 3, 4];
const a = arr[0];
const b = arr[1];
const c = arr[2];

const [x, y, z] = arr;
console.log(x); // 2
console.log(y); // 3
console.log(z); // 4
console.log(arr); // [2, 3, 4]


const restaurant = {
name: 'Classico Italiano',
location: 'Via Angelo Tavanti 23, Firenze, Italy',
categories: ['Italian', 'Pizzeria', 'Vegetarian', 'Organic'],
starterMenu: ['Focaccia', 'Bruschetta', 'Garlic Bread', 'Caprese Salad'],
mainMenu: ['Pizza', 'Pasta', 'Risotto'],
order: function (starterIndex, mainIndex) {
return [this.starterMenu[starterIndex], this.mainMenu[mainIndex]];
}
};
const [first, second] = restaurant.categories;
console.log(first); // Italian
console.log(second); // Pizzeria

let [main, , secondary] = restaurant.categories;
console.log(main); // Italian
console.log(secondary); // Vegetarian

////// swicth values

// const temp = main;
// main = secondary;
// secondary = temp;
// console.log(main); // Vegetarian
// console.log(secondary); // Italian

console.log(main, secondary);
[main, secondary] = [secondary, main];
console.log(main, secondary);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// receive 2 return values from a function
const restaurant1 = {
name: 'Classico Italiano',
location: 'Via Angelo Tavanti 23, Firenze, Italy',
categories: ['Italian', 'Pizzeria', 'Vegetarian', 'Organic'],
starterMenu: ['Focaccia', 'Bruschetta', 'Garlic Bread', 'Caprese Salad'],
mainMenu: ['Pizza', 'Pasta', 'Risotto'],
order: function (starterIndex, mainIndex) {
return [this.starterMenu[starterIndex], this.mainMenu[mainIndex]];
}
};

//  ['Garlic Bread', 'Pizza']
// console.log(restaurant.order(2, 0));
const [starter, mainCourse] = restaurant1.order(2, 0);
console.log(starter, mainCourse); // Garlic Bread Pizza
1
2
3
4
5
6
7
8
// nested destructuring
const nested = [2, 4, [5, 6]];

const [firstN, , nestedArray] = nested;
console.log(firstN, nestedArray); // 2 [5, 6]

const [i, , [j, k]] = nested;
console.log(i, j, k); // 2 5 6
1
2
3
4
5
6
// Default values
const [p, q, r] = [8, 9];
console.log(p, q, r); // 8 9 undefined

const [l, m, n = 1] = [8, 9];
console.log(l, m, n); // 8 9 1

destructuring object

get data from api

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
const restaurant = {
name: 'Classico Italiano',
location: 'Via Angelo Tavanti 23, Firenze, Italy',
categories: ['Italian', 'Pizzeria', 'Vegetarian', 'Organic'],
starterMenu: ['Focaccia', 'Bruschetta', 'Garlic Bread', 'Caprese Salad'],
mainMenu: ['Pizza', 'Pasta', 'Risotto'],

openingHours: {
thu: {
open: 12,
close: 22,
},
fri: {
open: 11,
close: 23,
},
sat: {
open: 0, // Open 24 hours
close: 24,
},
},
order: function (starterIndex, mainIndex) {
return [this.starterMenu[starterIndex], this.mainMenu[mainIndex]];
},
orderDelivery: function (obj) {
console.log(obj);
},
orderDelivery1: function ({ starterIndex = 1, mainIndex = 0, time = '20:00', address }) {
console.log(starterIndex, mainIndex, time, address);
console.log(`Order received! ${this.starterMenu[starterIndex]} and ${this.mainMenu[mainIndex]} will be delivered to ${address} at ${time}`);
},
};

restaurant.orderDelivery({
time: '22:30',
address: 'via del sole, 21',
mainIndex: 2,
starterIndex: 2,
})

// 2 2 '22:30' 'via del sole, 21'
// Order received! Garlic Bread and Risotto will be delivered to via del sole, 21 at 22:30
restaurant.orderDelivery1({
time: '22:30',
address: 'via del sole, 21',
mainIndex: 2,
starterIndex: 2,
})

// 2 2 '22:30' 'via del sole, 21'
// Order received! Garlic Bread and Risotto will be delivered to via del sole, 21 at 22:30
restaurant.orderDelivery1({
time: '22:30',
address: 'via del sole, 21',
mainIndex: 2,
starterIndex: 2,
})

// 1 0 '20:00' 'via del ssdole, 21'
// Order received! Bruschetta and Pizza will be delivered to via del ssdole, 21 at 20:00
restaurant.orderDelivery1({
address: 'via del ssdole, 21',
starterIndex: 1,
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102

const restaurant = {
name: 'Classico Italiano',
location: 'Via Angelo Tavanti 23, Firenze, Italy',
categories: ['Italian', 'Pizzeria', 'Vegetarian', 'Organic'],
starterMenu: ['Focaccia', 'Bruschetta', 'Garlic Bread', 'Caprese Salad'],
mainMenu: ['Pizza', 'Pasta', 'Risotto'],

openingHours: {
thu: {
open: 12,
close: 22,
},
fri: {
open: 11,
close: 23,
},
sat: {
open: 0, // Open 24 hours
close: 24,
},
},
order: function (starterIndex, mainIndex) {
return [this.starterMenu[starterIndex], this.mainMenu[mainIndex]];
},
orderDelivery: function (obj) {
console.log(obj);
},
orderDelivery1: function ({ starterIndex = 1, mainIndex = 0, time = "20:00", address }) {
console.log(starterIndex, mainIndex, time, address);
console.log(`Order received! ${this.starterMenu[starterIndex]} and ${this.mainMenu[mainIndex]} will be delivered to ${address} at ${time}`);
},
};

restaurant.orderDelivery({
time: '22:30',
address: 'via del sole, 21',
mainIndex: 2,
starterIndex: 2,
})

// 2 2 '22:30' 'via del sole, 21'
// Order received! Garlic Bread and Risotto will be delivered to via del sole, 21 at 22:30
restaurant.orderDelivery1({
time: '22:30',
address: 'via del sole, 21',
mainIndex: 2,
starterIndex: 2,
})


// 1 0 '20:00' 'via del ssdole, 21'
// Order received! Bruschetta and Pizza will be delivered to via del ssdole, 21 at 20:00
restaurant.orderDelivery1({
address: 'via del ssdole, 21',
starterIndex: 1,
})

const { name, openingHours, categories } = restaurant;

// Classico Italiano
// {thu: {…}, fri: {…}, sat: {…}}
// (4) ['Italian', 'Pizzeria', 'Vegetarian', 'Organic']
console.log(name, openingHours, categories);


const { name: restaurantName, openingHours: hours, categories: tags } = restaurant;
// // Classico Italiano
// {thu: {…}, fri: {…}, sat: {…}}
// (4) ['Italian', 'Pizzeria', 'Vegetarian', 'Organic']
console.log(restaurantName, hours, tags);

// default values
const { menu = [], starterMenu: starters = [] } = restaurant;
// []
// (4) ['Focaccia', 'Bruschetta', 'Garlic Bread', 'Caprese Salad']
console.log(menu, starters);

// mutating variables

let a = 111;
let b = 999;
const obj = { a: 23, b: 7, c: 14 };
// Uncaught SyntaxError: Unexpected token '='
// { a, b } = obj;

({ a, b } = obj);
// 23 7
console.log(a, b);


// nested objects
const { fri } = openingHours;
// {open: 11, close: 23}
console.log(fri);

const { fri: { open, close } } = openingHours;
console.log(open, close); // 11 23

const { fri: { open: o, close: c } } = openingHours;
console.log(o, c); // 11 23

spread operator

right side of asignment operator

get all the values spreadly

used only when
in array
function arguments

works on all iterables
arrays stirng maps, sets, NOT objects

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90

const arr = [7, 8, 9];
const badNewArr = [1, 2, arr[0], arr[1], arr[2]];
console.log(badNewArr);

const newArr = [1, 2, ...arr];
// [1, 2, 7, 8, 9]
console.log(newArr);

// 1 2 7 8 9
console.log(...newArr);


const restaurant = {
name: 'Classico Italiano',
location: 'Via Angelo Tavanti 23, Firenze, Italy',
categories: ['Italian', 'Pizzeria', 'Vegetarian', 'Organic'],
starterMenu: ['Focaccia', 'Bruschetta', 'Garlic Bread', 'Caprese Salad'],
mainMenu: ['Pizza', 'Pasta', 'Risotto'],

openingHours: {
thu: {
open: 12,
close: 22,
},
fri: {
open: 11,
close: 23,
},
sat: {
open: 0, // Open 24 hours
close: 24,
},
},
order: function (starterIndex, mainIndex) {
return [this.starterMenu[starterIndex], this.mainMenu[mainIndex]];
},
orderDelivery: function ({ starterIndex = 1, mainIndex = 0, time = "20:00", address }) {
console.log(starterIndex, mainIndex, time, address);
console.log(`Order received! ${this.starterMenu[starterIndex]} and ${this.mainMenu[mainIndex]} will be delivered to ${address} at ${time}`);
},
orderPasta: function (ing1, ing2, ing3) {
console.log(ing1, ing2, ing3);
console.log(`Here is your delicious pasta with ${ing1}, ${ing2}, ${ing3}`);
},
};


// ['Pizza', 'Pasta', 'Risotto', 'Gnocci']
const newMenu = [...restaurant.mainMenu, 'Gnocci'];
console.log(newMenu);

// copy array(shallow)

const mianMenuCopy = [...restaurant.mainMenu];

// join 2 arrays

const menu = [...restaurant.mainMenu, ...restaurant.starterMenu]
// ['Pizza', 'Pasta', 'Risotto', 'Focaccia', 'Bruschetta', 'Garlic Bread', 'Caprese Salad']
console.log(menu);

const str = 'Jonas';
const letters = [...str, ' ', 'S.'];
// ['J', 'o', 'n', 'a', 's', ' ', 'S.']
console.log(letters);

//
// const ingredients = [prompt('Let\'s make pasta! Ingredient 1?'), prompt('Let\'s make pasta! Ingredient 2?'), prompt('Let\'s make pasta! Ingredient 3?')]
const ingredients = ['q', 'q', 'q'];
console.log(ingredients);

// Here is your delicious pasta with q, q, q
restaurant.orderPasta(...ingredients);

// Object

const newRestaurant = { ...restaurant, founder: 'Guiseppe' };
// {name: 'Classico Italiano', location: 'Via Angelo Tavanti 23, Firenze, Italy', categories: Array(4), starterMenu: Array(4), mainMenu: Array(3), …}
console.log(newRestaurant);


// shalow copy
const restaurantCoy = { ...restaurant };
// Classico Italiano
console.log(restaurant.name);
restaurantCoy.name = 'Ristorant roma';
// Classico Italiano
console.log(restaurant.name);

rest pattern

left side of asignment operator

collect rest elemnets

always in the last place and only one

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97

//// array

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

const [a, b, ...others] = [1, 2, 3, 4, 5];
// 1 2 [3, 4, 5]
console.log(a, b, others);


const restaurant = {
name: 'Classico Italiano',
location: 'Via Angelo Tavanti 23, Firenze, Italy',
categories: ['Italian', 'Pizzeria', 'Vegetarian', 'Organic'],
starterMenu: ['Focaccia', 'Bruschetta', 'Garlic Bread', 'Caprese Salad'],
mainMenu: ['Pizza', 'Pasta', 'Risotto'],

openingHours: {
thu: {
open: 12,
close: 22,
},
fri: {
open: 11,
close: 23,
},
sat: {
open: 0, // Open 24 hours
close: 24,
},
},
order: function (starterIndex, mainIndex) {
return [this.starterMenu[starterIndex], this.mainMenu[mainIndex]];
},
orderDelivery: function ({ starterIndex = 1, mainIndex = 0, time = "20:00", address }) {
console.log(starterIndex, mainIndex, time, address);
console.log(`Order received! ${this.starterMenu[starterIndex]} and ${this.mainMenu[mainIndex]} will be delivered to ${address} at ${time}`);
},
orderPasta: function (ing1, ing2, ing3) {
console.log(ing1, ing2, ing3);
console.log(`Here is your delicious pasta with ${ing1}, ${ing2}, ${ing3}`);
},
orderPizza: function (mianIngredient, ...otherIngredients) {
console.log(mianIngredient, otherIngredients);
}
};


const [pizza, , risotto, ...otherFood] = [...restaurant.mainMenu, ...restaurant.starterMenu];
// Pizza
// Risotto
// ['Focaccia', 'Bruschetta', 'Garlic Bread', 'Caprese Salad']
console.log(pizza, risotto, otherFood);

// objects

const { sat, ...weekdays } = restaurant.openingHours;
// {thu: {…}, fri: {…}}
console.log(weekdays);

///// function

const add = function (...numbers) {
console.log(numbers);
let sum = 0;
for (let i = 0; i < numbers.length; i++) {
sum += numbers[i];
}
console.log(sum);
}

// [2, 3]
//5
add(2, 3);
// [5, 3, 7, 4]
// 19
add(5, 3, 7, 4);
/// [5, 3, 7, 4, 2, 3, 1]
// 35
add(5, 3, 7, 4, 2, 3, 1);


const x = [23, 5, 7];

// [23, 5, 7]
// 35
add(...x);


// mushrooms
// ['onion', 'olives', 'spinach']
restaurant.orderPizza('mushrooms', 'onion', 'olives', 'spinach');
// mushrooms
// []
restaurant.orderPizza('mushrooms');

short circuiting

||
if the first value is a turthy value, it will imediately return the first value

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71

// 3
console.log(3 || 'jonas'); // 3
console.log('' || 'jonas'); // jonas
console.log(true || 0); // true
console.log(undefined || null); // null
// Hello
console.log(undefined || 0 || '' || 'Hello' || 23 || null);


const restaurant = {
name: 'Classico Italiano',
location: 'Via Angelo Tavanti 23, Firenze, Italy',
categories: ['Italian', 'Pizzeria', 'Vegetarian', 'Organic'],
starterMenu: ['Focaccia', 'Bruschetta', 'Garlic Bread', 'Caprese Salad'],
mainMenu: ['Pizza', 'Pasta', 'Risotto'],

openingHours: {
thu: {
open: 12,
close: 22,
},
fri: {
open: 11,
close: 23,
},
sat: {
open: 0, // Open 24 hours
close: 24,
},
},
order: function (starterIndex, mainIndex) {
return [this.starterMenu[starterIndex], this.mainMenu[mainIndex]];
},
orderDelivery: function ({ starterIndex = 1, mainIndex = 0, time = "20:00", address }) {
console.log(starterIndex, mainIndex, time, address);
console.log(`Order received! ${this.starterMenu[starterIndex]} and ${this.mainMenu[mainIndex]} will be delivered to ${address} at ${time}`);
},
orderPasta: function (ing1, ing2, ing3) {
console.log(ing1, ing2, ing3);
console.log(`Here is your delicious pasta with ${ing1}, ${ing2}, ${ing3}`);
},
orderPizza: function (mianIngredient, ...otherIngredients) {
console.log(mianIngredient, otherIngredients);
}
};

const guests1 = restaurant.numGuests ? restaurant.numGuests : 10;
console.log(guests1); // 10

const guests2 = restaurant.numGuests || 10;
console.log(guests2); // 10

restaurant.numGuests = 23;
const guests3 = restaurant.numGuests || 10;
console.log(guests3); // 23

////////////////

//////// AND

console.log(0 && 'Jonas'); // 0
console.log(7 && 'Jonas'); // Jonas
console.log('Hello' && 23 && null && 'jonas'); // null

if (restaurant.orderPizza) {
restaurant.orderPizza('mushrooms', 'spinach');
}

restaurant.orderPizza && restaurant.orderPizza('mushrooms', 'spinach');

nullish coalescing oparator

1
2
3
4
5
6
7
// if restaurant.numGuests = 0
const guests2 = restaurant.numGuests || 10;
console.log(guests2); // 10

// nullish: nul and undefined (NOT or '')
const guestsCorrect = restaurant.numGuests ?? 10;
console.log(guestsCorrect);

logical assignment operators(es2021)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37


const rest1 = {
name: 'Capri',
numGuests: 20,
// numGuests: 0,
};
const rest2 = {
name: 'La Piazza',
owner: 'Giavanni Rossi',
};

// rest2.numGuests = rest2.numGuests || 10;
// rest1.numGuests = rest1.numGuests || 10;
// console.log(rest1, rest2);

// rest1.numGuests ||= 10;
// rest2.numGuests ||= 10;
// console.log(rest1, rest2);


// {name: 'Capri', numGuests: 20}
// {name: 'La Piazza', owner: 'Giavanni Rossi', numGuests: 10}
rest1.numGuests ??= 10;
rest2.numGuests ??= 10;
console.log(rest1, rest2);

// {name: 'Capri', numGuests: 20, owner: undefined}
// {name: 'La Piazza', owner: '<Anon>', numGuests: 10}
//
rest1.owner = rest1.owner && '<Anon>'
rest2.owner = rest2.owner && '<Anon>'
console.log(rest1, rest2);

rest1.owner &&= '<Anon>'
rest2.owner &&= '<Anon>'
console.log(rest1, rest2);

for of loop

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

const restaurant = {
name: 'Classico Italiano',
location: 'Via Angelo Tavanti 23, Firenze, Italy',
categories: ['Italian', 'Pizzeria', 'Vegetarian', 'Organic'],
starterMenu: ['Focaccia', 'Bruschetta', 'Garlic Bread', 'Caprese Salad'],
mainMenu: ['Pizza', 'Pasta', 'Risotto'],

};

const menu = [...restaurant.starterMenu, ...restaurant.mainMenu];
console.log(menu);


// Focaccia
// Bruschetta
// Garlic Bread
// Caprese Salad
// Pizza
// Pasta
// Risotto
for (const item of menu) {
console.log(item);
}
// can use break and continue


// Array Iterator {}
console.log(menu.entries());


for (const item of menu.entries()) {
// [0, 'Focaccia']
console.log(item);
// '1: Focaccia'
console.log(`${item[0] + 1}: ${item[1]}`);

}


for (const [i, e] of menu.entries()) {

// '1: Focaccia'
console.log(`${i + 1}: ${e}`);

}

enhanced object literals

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
const weekdays = ['mon', 'tue', 'wen', 'thu', 'fri', 'sat', 'sun'];

const openingHours = {
thu: {
open: 12,
close: 22,
},
// fri:
[weekdays[4]]: {
open: 11,
close: 23,
},
// day-6:
[`day-${2 + 4}`]: {
open: 0, // Open 24 hours
close: 24,
},
};
const restaurant = {
name: 'Classico Italiano',
location: 'Via Angelo Tavanti 23, Firenze, Italy',
categories: ['Italian', 'Pizzeria', 'Vegetarian', 'Organic'],
starterMenu: ['Focaccia', 'Bruschetta', 'Garlic Bread', 'Caprese Salad'],
mainMenu: ['Pizza', 'Pasta', 'Risotto'],

// openingHours: openingHours,
// es6 enhanced object literals
openingHours,

// order: function (starterIndex, mainIndex) {
// return [this.starterMenu[starterIndex], this.mainMenu[mainIndex]];
// },

// es6 new syntax
order(starterIndex, mainIndex) {
return [this.starterMenu[starterIndex], this.mainMenu[mainIndex]];
},
}

optinal chaining (?.)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103

const weekdays = ['mon', 'tue', 'wen', 'thu', 'fri', 'sat', 'sun'];

const openingHours = {
thu: {
open: 12,
close: 22,
},
// fri:
[weekdays[4]]: {
open: 11,
close: 23,
},
// day-6:
[`day-${2 + 4}`]: {
open: 0, // Open 24 hours
close: 24,
},
sat: {
open: 0, // Open 24 hours
close: 24,
},
};

const restaurant = {
name: 'Classico Italiano',
location: 'Via Angelo Tavanti 23, Firenze, Italy',
categories: ['Italian', 'Pizzeria', 'Vegetarian', 'Organic'],
starterMenu: ['Focaccia', 'Bruschetta', 'Garlic Bread', 'Caprese Salad'],
mainMenu: ['Pizza', 'Pasta', 'Risotto'],

// openingHours: openingHours,
// es6 enhanced object literals
openingHours,

// order: function (starterIndex, mainIndex) {
// return [this.starterMenu[starterIndex], this.mainMenu[mainIndex]];
// },

// es6 new syntax
order(starterIndex, mainIndex) {
return [this.starterMenu[starterIndex], this.mainMenu[mainIndex]];
},

};


// undefined
console.log(restaurant.openingHours.mon);
// script.js:725 Uncaught TypeError: Cannot read properties of undefined (reading 'open')
// console.log(restaurant.openingHours.mon.open);
// undefined
if (restaurant.openingHours && restaurant.openingHours.mon) console.log(restaurant.openingHours.mon.open);

// with optional chaining

// if (restaurant.openingHours.mon) console.log(restaurant.openingHours.mon.open);
console.log(restaurant.openingHours.mon?.open);

// if (restaurant.openingHours && restaurant.openingHours.mon) console.log(restaurant.openingHours.mon.open);
console.log(restaurant.openingHours?.mon?.open);


const days = ['mon', 'tue', 'wen', 'thu', 'fri', 'sat', 'sun'];


// On mon, we open at closed
// On tue, we open at closed
// On wen, we open at closed
// On thu, we open at 12
// On fri, we open at 11
// On sat, we open at 0
// On sun, we open at closed
for (const day of days) {
// console.log(day);
const open = (restaurant.openingHours[day]?.open) ?? 'closed';
console.log(`On ${day}, we open at ${open}`);
}

///// on methods
//  ['Focaccia', 'Pasta']
console.log(restaurant.order?.(0, 1) ?? 'Method does not exist');
// Method does not exist
console.log(restaurant.orderRisotto?.(0, 1) ?? 'Method does not exist');


///// on arrays (empty)

const users = [
{
name: 'Jonas',
email: 'hello@jonas.com',
}
];


// if(users.length > 0) console.log(users[0].name);
// else console.log('User array empty');
// Jonas
console.log(users[0]?.name ?? 'User array empty');

// User array empty
console.log(users[1]?.name ?? 'User array empty');

loop object key value entries

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74

const openingHours = {
thu: {
open: 12,
close: 22,
},
// fri:
fri: {
open: 11,
close: 23,
},
// day-6:
[`day-${2 + 4}`]: {
open: 0, // Open 24 hours
close: 24,
},
sat: {
open: 0, // Open 24 hours
close: 24,
},
};

////////// property name

//  ['thu', 'fri', 'day-6', 'sat']
console.log(Object.keys(openingHours));

let openStr = `We are open on ${Object.keys(openingHours).length} days: `;
// thu
// fri
// day-6
// sat
for (const day of Object.keys(openingHours)) {
// console.log(day);
openStr += `${day},`;
}

// We are open on 4 days: thu,fri,day-6,sat,
console.log(openStr);

////// properties value

// [{…}, {…}, {…}, {…}]
// 0: {open: 12, close: 22}
// 1: {open: 11, close: 23}
// 2: {open: 0, close: 24}
// 3: {open: 0, close: 24}
const values = Object.values(openingHours);
console.log(values);


/////// objec enties

// [Array(2), Array(2), Array(2), Array(2)]
// 0: (2) ['thu', {…}]
// 1: (2) ['fri', {…}]
// 2: (2) ['day-6', {…}]
// 3: (2) ['sat', {…}]
const entries = Object.entries(openingHours);
console.log(entries);

for (const [key, value] of Object.entries(openingHours)) {
// console.log(key, value);

//On thu we open at 12 and close at 22
console.log(`On ${key} we open at ${value?.open} and close at ${value?.close}`);
}

for (const [key, { open, close }] of Object.entries(openingHours)) {
// console.log(key, value);

//On thu we open at 12 and close at 22
console.log(`On ${key} we open at ${open} and close at ${close}`);
}

sets (unique elements)

order of sets is irrelevant
no way to get only one element from a set

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56

const ordersSet = new Set([
'Pasta',
'Pizza',
'Pizza',
'Risotto',
'Pasta',
'Pizza',
]);


// Set(3) {'Pasta', 'Pizza', 'Risotto'}
console.log(ordersSet);
// Set(5) {'J', 'o', 'n', 'a', 's'}
console.log(new Set("Jonas"));

console.log(ordersSet.size); // 3

console.log(ordersSet.has('Pizza')); // true
console.log(ordersSet.has('Bread')); // false


ordersSet.add('Garlic Bread');
ordersSet.add('Garlic Bread');
// Set(4) {'Pasta', 'Pizza', 'Risotto', 'Garlic Bread'}
console.log(ordersSet);

ordersSet.delete('Garlic Bread')
// Set(3) {'Pasta', 'Pizza', 'Risotto'}
console.log(ordersSet);

// ordersSet.clear();
// // Set(0) {size: 0}
// console.log(ordersSet);


// Pasta
// Pizza
// Risotto
for (const order of ordersSet) {
console.log(order);
}

///example

const staff = ['Waiter', 'Chef', 'Waiter', 'Manager', 'Chef', 'Waiter'];
const staffUnique = new Set(staff);
console.log(staffUnique);

const staffUniqueArr = [...new Set(staff)]
// ['Waiter', 'Chef', 'Manager']
console.log(staffUniqueArr);

// 3
console.log(new Set(['Waiter', 'Chef', 'Waiter', 'Manager', 'Chef', 'Waiter']).size);

maps

key can be any type of data

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
const rest = new Map();
rest.set('name', 'Classic italinano');
rest.set(1, 'Firenze, Italy');
rest.set(2, 'Firenze, Italy');
// Map(3) {'name' => 'Classic italinano', 1 => 'Firenze, Italy', 2 => 'Firenze, Italy'}
console.log(rest);

rest
.set('categories', ['Italian', 'Piszzeria', 'Vegetarian', 'Organic'])
.set('open', 11)
.set('close', 23).
set(true, 'We are open!')
.set(false, 'We are closed');
// Map(5) {'name' => 'Classic italinano', 1 => 'Firenze, Italy', 2 => 'Firenze, Italy', 'categories' => Array(4), 'open' => 11, ...}
console.log(rest);

console.log(rest.get('name')); // Classic italinano
console.log(rest.get(1)); // Firenze, Italy

const time = 21;
// We are open!
console.log(rest.get(time > rest.get('open') && time < rest.get('close')));

console.log(rest.has('categories'));
rest.delete(2)
console.log(rest);
console.log(rest.size); // 7
rest.clear();

////// array as key
rest.set([1, 2], 'TEST');
// Map(1) {Array(2) => 'TEST'}
console.log(rest);

// not the same array [1, 2]
// undefined
console.log(rest.get([1, 2]));

const arr = [1, 2]
rest.set(arr, 'TEST');
// Map(1) {Array(2) => 'TEST'}
console.log(rest);

// TEST
console.log(rest.get(arr));


//// object as key
rest.set(document.querySelector('h1'), 'Heading')
console.log(rest);

maps: iteration

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
const question = new Map([
['question', 'Waht is the best programmong language in the world?'],
[1, 'C'],
[2, 'java'],
[3, 'javascritp'],
['correct', 3],
[true, 'you are Correct!'],
[false, 'Try again'],
]);
// Map(7) {'question' => 'Waht is the best programmong language in the world?', 1 => 'C', 2 => 'java', 3 => 'javascritp', 'correct' => 3, …}
console.log(question);


const openingHours = {
thu: {
open: 12,
close: 22,
},
// fri:
fri: {
open: 11,
close: 23,
},
// day-6:
[`day-${2 + 4}`]: {
open: 0, // Open 24 hours
close: 24,
},
sat: {
open: 0, // Open 24 hours
close: 24,
},
};

// (4) [Array(2), Array(2), Array(2), Array(2)]
// 0: (2) ['thu', {…}]
// 1: (2) ['fri', {…}]
// 2: (2) ['day-6', {…}]
// 3: (2) ['sat', {…}]
console.log(Object.entries(openingHours));

// covert object ot map

const hoursMap = new Map(Object.entries(openingHours));
// Map(4) {'thu' => {…}, 'fri' => {…}, 'day-6' => {…}, 'sat' => {…}}
console.log(hoursMap);

// Answer 1: C
// Answer 2: java
// Answer 3: javascritp
for (const [key, value] of question) {
if (typeof key === 'number') {
console.log(`Answer ${key}: ${value}`);
}
}

// input '3'
const answer = 1;
// const answer = Number(prompt('Your answer: '));
console.log(answer);

// you are Correct!
console.log(question.get(answer === question.get('correct')));

////////convert map to an arrary

// [
// ['question', 'Waht is the best programmong language in the world?'],
// [1, 'C'],
// [2, 'java'],
// [3, 'javascritp'],
// ['correct', 3],
// [true, 'you are Correct!'],
// [false, 'Try again'],
// ]
console.log('...question', [...question]);
// MapIterator {'question', 1, 2, 3, 'correct', …}
console.log(question.keys());
// MapIterator {'Waht is the best programmong language in the world?', 'C', 'java', 'javascritp', 3, …}
console.log(question.values());
// MapIterator {'question' => 'Waht is the best programmong language in the world?', 1 => 'C', 2 => 'java', 3 => 'javascritp', 'correct' => 3, …}
console.log(question.entries());

which data structure to use

sources of data

  1. from the program iteself: data written directly in source code (e.g. status messages)
  2. from the ui: data input from the user or data written in DOM(e.g. tasks in todo app)
  3. From external sources: data fetched for example from web api(e.g. rcipe objects)

simple list
key/value pairs: key allow us to describe values

array <> sets
array:

  • use when you need ordered list of values(might contain duplicates)
  • use when you need to manipulate data

sets

  • use when you need to work with unique values
  • use when high-performance is really important
  • use to remove duplicates form arrays

objects

  • more ‘traditonal’ key/value store (‘abused’ object)

  • easier to write and access value with . and []

  • use when you need to include functions(methods)

  • use when working with json (can convert to map)

maps

  • better performance

  • keys can have any data type

  • easy to iterate

  • easy to compute size

  • use when you simply need to map key to values

  • use whe you need keys that are not strings

String methods

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44


const airline = 'TAP Air Portugal';
const plane = 'A320';

console.log(plane[0]); // 'A'
console.log(plane[1]); // '3'
console.log(plane[2]); // '2'
console.log('B737'[1]); // '7'

console.log(airline.length); // 16
console.log('airline'.length); // 7

// first occurance
console.log(airline.indexOf('r')); // 6
console.log(airline.indexOf('Portugal')); // 8
console.log(airline.indexOf('portugal')); // -1
// last occurance
console.log(airline.lastIndexOf('r')); // 10

console.log(airline.slice(4)); // Air Portugal
console.log(airline.slice(4, 7)); // 'Air'
console.log(airline); // 'TAP Air Portugal'

console.log(airline.slice(0, airline.indexOf(' '))); // 'TAP'
console.log(airline.slice(airline.lastIndexOf(' ') + 1)); // 'Portugal'
console.log(airline.slice(-2)); // al
console.log(airline.slice(1, -1)); // AP Air Portuga

// B AND E are middle seats
const checkMiddleSeat = function (seat) {
const s = seat.slice(-1);
if (s === 'B' || s === 'E') {
console.log('you got the middle seat');
} else {
console.log('YOu got lucky');
}
}
// you got the middle seat
checkMiddleSeat('11B')
// YOu got lucky
checkMiddleSeat('23C')
// you got the middle seat
checkMiddleSeat('3E')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67


const airline = 'TAP Air Portugal';


console.log(airline.toLowerCase()); // tap air portugal
console.log(airline.toUpperCase()); // TAP AIR PORTUGAL

// fix capitalization in name
const passenger = 'jOnAS';
const passengerLower = passenger.toLowerCase();
const passengerCorrect = passengerLower[0].toUpperCase() + passengerLower.slice(1);
console.log(passengerCorrect); // Jonas

// comparing email
const email = 'hello@jonas.io';
const loginEmail = ' Hello@Jonas.Io \n';

// const trimEmail = loginEmail.trim();
// console.log(trimEmail); // 'Hello@Jonas.Io'
// const lowerEmail = trimEmail.toLowerCase();

const normalizedEmail = loginEmail.trim().toLowerCase();
console.log(normalizedEmail); // 'hello@jonas.io'

// replacing

const priceGB = '288,97¥';
const priceUs = priceGB.replace('¥', '$').replace(',', '.');
console.log(priceUs); // 288.97$

const announcement = 'All passengers come to barding door 23, Bording door 23!'
// just the first occurance
// All passengers come to barding gate 23, Bording door 23!
console.log(announcement.replace('door', 'gate'));

// regex global flag
// All passengers come to barding gate 23, Bording gate 23!
console.log(announcement.replace(/door/g, 'gate'));

// return BOOLEAN value

const plane = 'Airbus A320neo';
console.log(plane.includes('A320')); // true
console.log(plane.includes('Boeing')); // false
console.log(plane.startsWith('Airbus')); // true

if (plane.startsWith('Airbus') && plane.endsWith('neo')) {
console.log('Part of the new airbus family');
}

/// exercise

const checkBaggage = function (items) {
const baggage = items.toLowerCase();
if (baggage.includes('knife') || baggage.includes('gun')) {
console.log('You are not allowed on board');
} else {
console.log('Welcome aboard');
}
}
// You are not allowed on board
checkBaggage('I have a laptop, some Food and pocket Knife');
// Welcome aboard
checkBaggage('Sock and camera');
// You are not allowed on board
checkBaggage('Got some snacks and a gun for protection');
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69

////// split and join

// (4) ['a', 'very', 'nice', 'string']
console.log('a+very+nice+string'.split('+'));
//  ['Jonas', 'Schmedtmann']
console.log('Jonas Schmedtmann'.split(' '));
const [firstName, lastName] = 'Jonas Schmedtmann'.split(' ');
// Jonas Schmedtmann
console.log(firstName, lastName);


const newName = ['Mr.', firstName, lastName.toUpperCase()].join(' ');
console.log(newName); // 'Mr. Jonas SCHMEDTMANN'

const capitalizdName = function (name) {
name = name.toLowerCase();
const names = name.split(' ');
const newName = []
for (const n of names) {
// const nNew = n[0].toUpperCase() + n.slice(1);

const nNew = n.replace(n[0], n[0].toUpperCase());
newName.push(nNew);
}
return newName.join(' ');
}

const passenger = 'jessica ann simth daivs';
// Jessica Ann Simth Daivs
console.log(capitalizdName(passenger));

///// padding
// Pads the current string with a given string (possibly repeated) so that the resulting string reaches a given length.

const message = 'Go to gete 23!';
// '+++++++++++Go to gete 23!'
console.log(message.padStart(25, "+"));
// Go to gete 23!+++++++++++
console.log(message.padEnd(25, "+"));

const maskCreditCard = function (number) {
const str = number + '';
const last = str.slice(-4);
return last.padStart(str.length, '*')

}

// '***********8965'
console.log(maskCreditCard(457814236578965));
// *****8965
console.log(maskCreditCard('236578965'));


/////repeat

const message2 = 'Bad weather... All Departures Delayed...';
// Bad weather... All Departures Delayed...Bad weather... All Departures Delayed...Bad weather... All Departures Delayed...
console.log(message2.repeat(3));

const planeInline = function (n) {
console.log(`There are ${n} plane in line ${'✈️'.repeat(n)}`);
}
// There are 5 plane in line ✈️✈️✈️✈️✈️
planeInline(5);
// There are 3 plane in line ✈️✈️✈️
planeInline(3);
// There are 12 plane in line ✈️✈️✈️✈️✈️✈️✈️✈️✈️✈️✈️✈️
planeInline(12);

function deep

default parameters

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

const bookings = []

// ES 6
// the express can contain the other param before the param
const createBooking = function (flightNum, numPassengers = 1 + 0, price = 199 * numPassengers) {

// ES 5
// numPassengers = numPassengers || 1;
// price = price || 199;

const booking = {
flightNum,
numPassengers,
price,
}
console.log(booking);
bookings.push(booking);
}

// {flightNum: 'LH123', numPassengers: undefined, price: undefined}
// createBooking('LH123');


// ES 6
// {flightNum: 'LH123', numPassengers: 1, price: 199}
createBooking('LH123');

// {flightNum: 'LH123', numPassengers: 2, price: 800}
createBooking('LH123', 2, 800);

// {flightNum: 'LH123', numPassengers: 3, price: 597}
createBooking('LH123', 3);

// {flightNum: 'LH123', numPassengers: 1, price: 1000}
createBooking('LH123', undefined, 1000);

how passing arguments works: value vs reference

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40


const flight = 'LH234';
const jonas = {
name: 'Jonas Schmedtmann',
passport: 21346795,
}
const checkIn = function (flightNum, passenger) {
flightNum = 'LH999';
passenger.name = 'Mr.' + passenger.name;
if (passenger.passport === 21346795) {
console.log('Check In');
} else {
console.log('Wrong passport');
}
}

// LH234
// {name: 'Jonas Schmedtmann', passport: 21346795}
console.log(flight, jonas);

// same as doing ...
// const flightNum = flight;
// const passenger = jonas;
checkIn(flight, jonas);


// LH234
// {name: 'Mr.Jonas Schmedtmann', passport: 21346795}
console.log(flight, jonas);

///// example

const newPassport = function (person) {
person.passport = Math.trunc(Math.random() * 10000000000);
}

newPassport(jonas);
// Wrong passport
checkIn(flight, jonas);

first class and higher order functions

functions are simply values

functions are just another ‘type’ of object

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// store functions in variables or properties

const add = (a, b) => a + b;
const counter = {
value: 23,
inc: function () {
this.value++;
}
}

// pass functions as arguments to other functions
// addEventListenner is higher order function
// greet is the callback function
const greet = () => console.log('Hey Jonas');
btnClose.addEventListenner('click', greet);


// return functions form functions
// count is the higher order function
// // function(){ counter++; } is the returned function
function count() {
let counter = 0;
return function () {
counter++;
}
}

// call methods on functions
counter.inc.bind(someOtherObject);

high order functions

a function that receives anthoer function as an argument
a function that returns a new function
or both

functins accepting callback functions

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
const oneWord = function (str) {
return str.replace(/ /g, '').toLowerCase();
};

const upperFirstWord = function (str) {
const [first, ...others] = str.split(' ');
return [first.toUpperCase(), ...others].join(' ')
};

const transformer = function (str, fn) {
console.log(`Original string: ${str}`);
console.log(`Transformed string: ${fn(str)}`);
console.log(`Trnasformed by: ${fn.name}`);
}

// Original string: JavaScript is the best programming language!
// Transformed string: JAVASCRIPT is the best programming language!
// Trnasformed by: upperFirstWord
transformer('JavaScript is the best programming language!', upperFirstWord);

// Original string: JavaScript is the best programming language!
// Transformed string: javascriptisthebestprogramminglanguage!
// Trnasformed by: oneWord
transformer('JavaScript is the best programming language!', oneWord);

const high5 = function () {
console.log('😍');
}

document.body.addEventListener('click', high5);

['Jonas', 'Martha', 'Adam'].forEach(high5);

functions returning functions

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

const greet = function (greeting) {
return function (name) {
console.log(`${greeting} ${name}`);
}
}

const greeterHey = greet('Hey');
greeterHey('Jonas');
greeterHey('Steven');


greet('Hey')('Jonas');

// functional programming

const greet1 = greeting => name => console.log(`${greeting} ${name}`);

the CALL and apply METHODS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55

const lufthansa = {
airline: 'Lufthansa',
iataCode: 'LH',
booking: [],
book(flightNum, name) {
console.log(`${name} booked a seat on ${this.airline} flight ${this.iataCode}${flightNum}`);
this.booking.push({
flight: `${this.iataCode}${flightNum}`,
name: name,
})
}
}
// Jonas Schmedtmann booked a seat on Lufthansa flight LH238
lufthansa.book(238, 'Jonas Schmedtmann');
// Jonas Smith booked a seat on Lufthansa flight LH635
lufthansa.book(635, 'Jonas Smith');

console.log(lufthansa.booking);


const eurowings = {
airline: 'Eurowings',
iataCode: 'EW',
booking: [],
}


const book = lufthansa.book;

// in strict mode
// regular functions call "this" is undefined
// Uncaught TypeError: Cannot read properties of undefined
// book(23, 'Sarah Williams');


// Sarah Williams booked a seat on Eurowings flight EW23
book.call(eurowings, 23, 'Sarah Williams');
// Mary cooper booked a seat on Lufthansa flight LH239
book.call(lufthansa, 239, 'Mary cooper');

const swiss = {
airline: 'Swiss Air Lines',
iataCode: 'LX',
booking: [],
}

// Mary cooper booked a seat on Swiss Air Lines flight LX556
book.call(swiss, 556, 'Mary cooper');

// Apply method
const flightData = [583, 'George Cooper'];
// George Cooper booked a seat on Swiss Air Lines flight LX583
book.apply(swiss, flightData);
book.call(swiss, ...flightData);

bind method

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65


const lufthansa = {
airline: 'Lufthansa',
iataCode: 'LH',
booking: [],
book(flightNum, name) {
console.log(`${name} booked a seat on ${this.airline} flight ${this.iataCode}${flightNum}`);
this.booking.push({
flight: `${this.iataCode}${flightNum}`,
name: name,
})
}
};


const eurowings = {
airline: 'Eurowings',
iataCode: 'EW',
booking: [],
};

const book = lufthansa.book;
const bookEW = book.bind(eurowings);

// Steven Williams booked a seat on Eurowings flight EW23
bookEW(23, 'Steven Williams');

/////// partial application
const bookEW23 = book.bind(eurowings, 23);
// John Smith booked a seat on Eurowings flight EW23
bookEW23('John Smith');
// Martha Cooper booked a seat on Eurowings flight EW23
bookEW23('Martha Cooper');

///// with Event Listeners

lufthansa.planes = 300;
lufthansa.buyPlane = function () {
this.planes++;
console.log(this, this.planes);
}

// button element
// NaN
document.querySelector('.buy').addEventListener('click', lufthansa.buyPlane)
//
document.querySelector('.buy').addEventListener('click', lufthansa.buyPlane.bind(lufthansa));

/////// partial application

const addTax = function (rate, value) {
return value + value * rate;
}
// 220
console.log(addTax(0.1, 200));

// addVAT = function (value) {
// return value + value * 0.23;
// }
// created a new function
const addVAT = addTax.bind(null, 0.23);

// 246
console.log(addVAT(200));

immediately invoked function expression

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

const runOnce = function () {
console.log('this will never run again');
}

runOnce();
runOnce();

// Uncaught SyntaxError: Function statements require a function name
// function() {
// console.log('this will never run again');
// }

(function () {
console.log('this will never run again');
// const isPrivate = 23;
})();

(() => console.log('this will also never run again'))();

// function scope

// Uncaught ReferenceError: isPrivate is not defined
// console.log(isPrivate);

{
const isPrivate = 23;
var notPrivate = 46;
}

// Uncaught ReferenceError: isPrivate is not defined
// console.log(isPrivate);
// 46
console.log(notPrivate);

closures

make functions remenber all the variables where it is birthed place.

a function has access to the variable environment(VE) of the execution context in which it was created.
even if it was gone.

closure: VE attached to the function, exactly as it was at the time and place the function was created

closure is priority over scope chain


less formal

a closure is the closed-over variable environment of the execution context in which a function was created, even after that execution context is gone;

a closure give a function aceess to all the variables of its parent function, even after that parent function has returned. The function keeps a reference to its outer scope, which preserves the scope chain throughout time.

a closeure makes sure that a function doesnt lose connection to variable that existed at the function’s birth place

a clsure is like a backpack that a function carries around wherever it goes. This backpack has all the variable that were present in the environment where the function was created.

we can not directly access a closure-over variables explicitly

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

const secureBooking = function () {
let passengerCount = 0;

return function () {
passengerCount++;
console.log(`${passengerCount} passengers`);
}
};

const booker = secureBooking();
// 1 passengers
booker();
// 2 passengers
booker();
// 3 passengers
booker();

console.dir(booker);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57

//// ///// example 1

let f;

const g = function () {
const a = 23;
f = function () {
console.log(a * 2);
}
}

const h = function () {
const b = 777;
f = function () {
console.log(b * 2);
}
}

g();
// 46
f();

console.dir(f)

// re-assigning f function
h();
// 1554
f();

console.dir(f)


///// example 2

const boardPassengers = function (n, wait) {
const perGoup = n / 3;

// 1s
setTimeout(function () {
console.log(`we are now boarding all ${n} passengers`);
console.log(`There are 3 groups, each with ${perGoup} passengers`);
}, wait * 1000);

console.log(`will start boading in ${wait} seconds`);
};

// 'will start boading in 3 seconds'
//////////////// 3s late
// 'we are now boarding all 180 passengers'
// 'There are 3 groups, each with 60 passengers'

// closure have high priority
const perGoup = 1000;

boardPassengers(180, 3);

1
2
3
4
5
6
7
(function () {
const header = document.querySelector('h1');
header.style.color = 'red';
document.body.addEventListener('click', function () {
header.style.color = 'blue';
});
})();

array deep

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61

let arr = ['a', 'b', 'c', 'd', 'e'];


// Returns a copy of a section of an array. For both start and end, a negative index can be used to indicate an offset from the end of the array. For example, -2 refers to the second to last element of the array.
// (3) ['c', 'd', 'e']
console.log(arr.slice(2));
// (2) ['c', 'd']
console.log(arr.slice(2, 4));
// (2) ['c', 'd']
console.log(arr.slice(-3, -1));
// (2) ['b', 'c']
console.log(arr.slice(1, -2));
// (2) ['d', 'e']
console.log(arr.slice(-2));
// copy
// (5) ['a', 'b', 'c', 'd', 'e']
console.log(arr.slice());
console.log([...arr]);

////////////// splice

// Removes elements from an array and, if necessary, inserts new elements in their place, returning the deleted elements.

// @param start — The zero-based location in the array from which to start removing elements.

// @param deleteCount — The number of elements to remove.

// @returns — An array containing the elements that were deleted.
// (3) ['c', 'd', 'e']
// console.log(arr.splice(2));

arr.splice(-1);

//  ['a', 'b', 'c', 'd']
console.log(arr);


/////////// reverse
// Reverses the elements in an array in place. This method mutates the array and returns a reference to the same array.

arr = ['a', 'b', 'c', 'd', 'e'];

const arr2 = ['j', 'i', 'h', 'g', 'f'];
// (5) ['f', 'g', 'h', 'i', 'j']
console.log(arr2.reverse());
// (5) ['f', 'g', 'h', 'i', 'j']
console.log(arr2);

//////////////// concat

const letters = arr.concat(arr2);
// (10) ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']
console.log(letters);
console.log([...arr, ...arr2]);

//////////////// join
// a - b - c - d - e - f - g - h - i - j
//
// a - b - c - d - e - f - g - h - i - j
console.log(letters.join(' - '));

at method

1
2
3
4
5
6
7
8
9
10
11
12
13
const arr = [23, 11, 64];

// 23
// console.log(arr[0]);
console.log(arr.at(0));

// 64
// console.log(arr[arr.length - 1]);
// console.log(arr.slice(-1)[0]);
console.log(arr.at(-1));

// 's'
console.log(`jonas`.at(-1));

foreach: looping arrays

foreach
cant break or continue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

const movements = [200, 450, -400, 3000, -650, -130, 70, 1300];

for (const movement of movements) {
if (movement > 0) {
console.log(`You deposited ${movement}`);
} else {
console.log(`You withdraw ${Math.abs(movement)}`);
}
}

console.log('------------------');

// (method) Array<number>.forEach(callbackfn: (value: number, index: number, array: number[]) => void, thisArg?: any): void
movements.forEach(function (movement) {
if (movement > 0) {
console.log(`You deposited ${movement}`);
} else {
console.log(`You withdraw ${Math.abs(movement)}`);
}
});

console.log('------------------');

for (const [i, movement] of movements.entries()) {
if (movement > 0) {
console.log(`Movement ${i + 1}: You deposited ${movement}`);
} else {
console.log(`Movement ${i + 1}: You withdraw ${Math.abs(movement)}`);
}
}

console.log('------------------');

movements.forEach(function (movement, i, arr) {
if (movement > 0) {
console.log(`Movement ${i + 1}: You deposited ${movement}`);
} else {
console.log(`Movement ${i + 1}: You withdraw ${Math.abs(movement)}`);
}
});

foreach with maps and sets

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

const currencies = new Map([
['USD', 'United States dollar'],
['EUR', 'Euro'],
['GBP', 'Pound sterling'],
]);


// (method) Map<string, string>.forEach(callbackfn: (value: string, key: string, map: Map<string, string>) => void, thisArg?: any): void
currencies.forEach(function (value, key, map) {
console.log(`${key}: ${value}`);
})

/////// sets

const currenciesUnique = new Set(['USD', 'GBP', 'USD', 'EUR', 'EUR']);

console.log(currenciesUnique);


// (method) Set<string>.forEach(callbackfn: (value: string, value2: string, set: Set<string>) => void, thisArg?: any): void
currenciesUnique.forEach(function (value, value2, map) {
console.log(`${value2}: ${value}`);
});

bankist project

alt text

create DOM element

1
2
3
4
5
6
7
8
9
10
11
const containerMovements = document.querySelector('.movements');
containerMovements.innerHTML = '';

const html = `
<div class="movements__row">
<div class="movements__type movements__type--${type}">${i + 1} ${type}</div>
<div class="movements__value">${mov}</div>
</div>
`;
// (method) Element.insertAdjacentHTML(position: InsertPosition, text: string): void
containerMovements.insertAdjacentHTML('afterbegin', html)

position
A string representing the position relative to the element. Must be one of the following strings

“beforebegin”
Before the element. Only valid if the element is in the DOM tree and has a parent element.

“afterbegin”
Just inside the element, before its first child.

“beforeend”
Just inside the element, after its last child.

“afterend”
After the element. Only valid if the element is in the DOM tree and has a parent element.

1
2
3
4
5
6
7
<!-- beforebegin -->
<p>
<!-- afterbegin -->
foo
<!-- beforeend -->
</p>
<!-- afterend -->

data transformations: map, filter, reduce

map
returns a new array containg the results of applying an operation on all origianl array elements.

filter
filter returns a new array containing the array elements that passed a specified test condition

reduce
boils(reduce) all array elements down to one single value(e.g. adding all elements together)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
const arr = [3, 1, 4, 3, 2];

// map(callbackfn: (value: number, index: number, array: number[]) => any, thisArg?: any): any[]
// A function that accepts up to three arguments. The map method calls the callbackfn function one time for each element in the array.
// Calls a defined callback function on each element of an array, and returns an array that contains the results.

const mapArr = arr.map(function (value) {
return value * 2;
})

// (5) [6, 2, 8, 6, 4]
console.log(mapArr);


// filter(predicate: (value: number, index: number, array: number[]) => value is number, thisArg?: any): number[]
// A function that accepts up to three arguments. The filter method calls the predicate function one time for each element in the array.
// Returns the elements of an array that meet the condition specified in a callback function.
const filteredArr = arr.filter(function (value) {
return value > 2;
});

// (3) [3, 4, 3]
console.log(filteredArr);

// reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number): number
// A function that accepts up to four arguments. The reduce method calls the callbackfn function one time for each element in the array.
// Calls the specified callback function for all the elements in an array. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function.
const reducedResult = arr.reduce(function (acc, current) {
return acc + current;
});

// 13
console.log(reducedResult);

map

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

const movements = [200, 450, -400, 3000, -650, -130, 70, 1300];


const eurToUsd = 1.1;

const movmentsUsd = movements.map(function (mov) {
return mov * eurToUsd;
});

console.log(movements);
console.log(movmentsUsd);

const movementsUsdFor = [];
for (const mov of movements) {
movementsUsdFor.push(mov * eurToUsd);
}
console.log(movementsUsdFor);

const movmentsUsd1 = movements.map(mov => mov * eurToUsd);
console.log(movmentsUsd1);

const movementsDescriptions = movements.map((mov, i) => `Movement ${i + 1}: You ${mov > 0 ? 'deposited' : 'withdraw'} ${Math.abs(mov)}`);

console.log(movementsDescriptions);

filter method

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const movements = [200, 450, -400, 3000, -650, -130, 70, 1300];

const deposits = movements.filter((mov) => {
return mov > 0;
});
// (5) [200, 450, 3000, 70, 1300]
console.log(deposits);

const deposits1 = [];
for (const mov of movements) {
if (mov > 0) {
deposits1.push(mov);
}
}
// (5) [200, 450, 3000, 70, 1300]
console.log(deposits1);

const withdrawals = movements.filter(mov => mov < 0);
// (3) [-400, -650, -130]
console.log(withdrawals);

the reduce method

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

const movements = [200, 450, -400, 3000, -650, -130, 70, 1300];

// accumulator
// (method) Array<number>.reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number (+2 overloads)
// @param initialValue — If initialValue is specified, it is used as the initial value to start the accumulation. The first call to the callbackfn function provides this value as an argument instead of an array value.
//
const balance = movements.reduce(function (acc, cur, i, arr) {
// Iteration 0: 0
// Iteration 1: 200
// Iteration 2: 650
// Iteration 3: 250
// Iteration 4: 3250
// Iteration 5: 2600
// Iteration 6: 2470
// Iteration 7: 2540
console.log(`Iteration ${i}: ${acc}`);
return acc + cur;
}, 0);
// 3840
console.log(balance);

let balance2 = 0;
for (const mov of movements) {
balance2 += mov;
}
console.log(balance2);
1
2
3
4
5
6
/// maximum value

const movements = [200, 450, -400, 3000, -650, -130, 70, 1300];

const max = movements.reduce((acc, mov) => acc < mov ? mov : acc, movements[0]);
console.log(max);

chainning methods

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

const eurToUsd = 1.1;
const movements = [200, 450, -400, 3000, -650, -130, 70, 1300];

// pipeline
const totalDepositesUsd = movements
.filter(mov => mov > 0)
// .map((mov, i, arr) => {
// console.log(arr);
// return mov * eurToUsd
// })
.map(mov => mov * eurToUsd)
.reduce((acc, mov) => acc + mov, 0);

// 5522.000000000001
console.log(totalDepositesUsd);

the find method

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

const movements = [200, 450, -400, 3000, -650, -130, 70, 1300];

// return the first element

// (method) Array<number>.find(predicate: (value: number, index: number, obj: number[]) => unknown, thisArg?: any): number | undefined (+1 overload)
// Returns the value of the first element in the array where predicate is true, and undefined otherwise.
// @param predicate
//find calls predicate once for each element of the array, in ascending order, until it finds one where predicate returns true. If such an element is found, find immediately returns that element value. Otherwise, find returns undefined.
const firstWithdrawal = movements.find(mov => mov < 0);
// -400
console.log(firstWithdrawal);


console.log(accounts);
const account = accounts.find(acc => acc.owner === 'Jessica Davis');
console.log(account);

// for of
let accountFor = {};
for (const acc of accounts) {
if (acc.owner === 'Jessica Davis') {
accountFor = acc;
}
}

console.log(accountFor);

bankist project login page

1
2
3
4
5
6
7
8
const btnLogin = document.querySelector('.login__btn');
btnLogin.addEventListener('click', function (e) {
// prevent form form submitting
e.preventDefault();
});

const inputLoginPin = document.querySelector('.login__input--pin');
inputClosePin.blur();

bankist project: implementing transfers

the findindex method

1
2
3
4
5
/// delete account
const index = accounts.findIndex(acc => acc.username === currentAccount.username)
// console.log(accounts);
accounts.splice(index, 1)
// console.log(accounts);

some and every methods

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

//////////some

const movements = [200, 450, -400, 3000, -650, -130, 70, 1300];

// only for elements value equal to arguments
console.log(movements.includes(-130));

const anyDeposits = movements.some(mov => mov > 5000);
console.log(anyDeposits);

///////every
// Determines whether all the members of an array satisfy the specified test.

// false
console.log(movements.every(mov => mov > 0));
// true
console.log(account4.movements.every(mov => mov > 0));

/////// separate callback

const deposit = mov => mov > 0;
console.log(movements.some(deposit));
console.log(movements.every(deposit));
console.log(movements.filter(deposit));

flat and flatmap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

const arr = [[1, 2, 3], [4, 5, 6], 7, 8];
// (8) [1, 2, 3, 4, 5, 6, 7, 8]
// Returns a new array with all sub-array elements concatenated into it recursively up to the specified depth.
// @param depth — The maximum recursion depth
console.log(arr.flat());

const arrDeep = [[1, 2, 3], [4, [5, 6]], 7, 8];
// (7) [1, 2, 3, 4, Array(2), 7, 8]
// console.log(arrDeep.flat());
// (8) [1, 2, 3, 4, 5, 6, 7, 8]
console.log(arrDeep.flat(2));

const accountsMovements = accounts.map(acc => acc.movements);
// (4) [Array(8), Array(8), Array(8), Array(5)]
// 0: (8) [200, 450, -400, 3000, -650, -130, 70, 1300]
// 1: (8) [5000, 3400, -150, -790, -3210, -1000, 8500, -30]
// 2: (8) [200, -200, 340, -300, -20, 50, 400, -460]
// 3: (5) [430, 1000, 700, 50, 90]
console.log(accountsMovements);

const allMovements = accountsMovements.flat();
// (29) [200, 450, -400, 3000, -650, -130, 70, 1300, 5000, 3400, -150, -790, -3210, -1000, 8500, -30, 200, -200, 340, -300, -20, 50, 400, -460, 430, 1000, 700, 50, 90]
console.log(allMovements);
const overallBalance = allMovements.reduce((acc, mov) => acc + mov, 0);
// 17840
console.log(overallBalance);

const overallBalanceChain = accounts
.map(acc => acc.movements)
.flat()
.reduce((acc, mov) => acc + mov, 0);
console.log(overallBalanceChain);

///////////////flatmap
/// only one level deep

const overallBalanceFlatMap = accounts
.flatMap(acc => acc.movements)
.reduce((acc, mov) => acc + mov, 0);
console.log(overallBalanceFlatMap);

sorting arrays

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
const owners = ['Jonas', 'Zach', 'Adam', 'Martha'];

// (method) Array<string>.sort(compareFn?: ((a: string, b: string) => number) | undefined): string[]
// Sorts an array in place. This method mutates the array and returns a reference to the same array.

// @param compareFn
// Function used to determine the order of the elements. It is expected to return a negative value if the first argument is less than the second argument, zero if they're equal, and a positive value otherwise. If omitted, the elements are sorted in ascending, ASCII character order.
// [11,2,22,1].sort((a, b) => a - b)
// (4) ['Adam', 'Jonas', 'Martha', 'Zach']
console.log(owners.sort());
// (4) ['Adam', 'Jonas', 'Martha', 'Zach']
console.log(owners);

//// Numbers
const movements = [200, 450, -400, 3000, -650, -130, 70, 1300];
// (8) [200, 450, -400, 3000, -650, -130, 70, 1300]
console.log(movements);
// // (8) [-130, -400, -650, 1300, 200, 3000, 450, 70]
// console.log(movements.sort());

// return < 0 , a, b
// return > 0 , b, a
// (8) [-650, -400, -130, 70, 200, 450, 1300, 3000]
console.log(movements.sort((a, b) => {
if (a > b) return 1;
if (b > a) return -1;
}));


// (8) [3000, 1300, 450, 200, 70, -130, -400, -650]
console.log(movements.sort((a, b) => {
if (a > b) return -1;
if (b > a) return 1;
}));

// (8) [-650, -400, -130, 70, 200, 450, 1300, 3000]
console.log(movements.sort((a, b) => a - b));
1
2
3
4
5
6
7
8
9
const dogs1 = [
{ weight: 22, curFood: 250, owners: ['Alice', 'Bob'] },
{ weight: 8, curFood: 200, owners: ['Matilda'] },
{ weight: 13, curFood: 275, owners: ['Sarah', 'John'] },
{ weight: 32, curFood: 340, owners: ['Michael'] },
];

const copyDogs1 = dogs1.slice().sort((a, b) => a.weight - b.weight);
console.log(copyDogs1);

creating and filling arrays

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55

console.log([1, 2, 3, 4, 5, 6, 7, 8]);
console.log(new Array(1, 2, 3, 4, 5, 6, 7, 8));

const x = new Array(7);
// (7) [empty × 7]
console.log(x);

// (7) [empty × 7]
// console.log(x.map(() => 5));


// Changes all array elements from start to end index to a static value and returns the modified array
// @param value — value to fill array section with
// @param start
// index to start filling the array at. If start is negative, it is treated as length+start where length is the length of the array.
// @param end
// index to stop filling the array at. If end is negative, it is treated as length+end.
x.fill(1);
// (7) [1, 1, 1, 1, 1, 1, 1]
console.log(x);

x.fill(2, 3, 5)
// (7) [1, 1, 1, 2, 2, 1, 1]
console.log(x);

//// Array.from


// (method) ArrayConstructor.from<any, number>(iterable: Iterable<any> | ArrayLike<any>, mapfn: (v: any, k: number) => number, thisArg?: any): number[] (+3 overloads)
// Creates an array from an iterable object.
// @param iterable — An iterable object to convert to an array.
// @param mapfn — A mapping function to call on every element of the array.
// (7) [1, 1, 1, 1, 1, 1, 1]
console.log((Array.from({ length: 7 }, () => 1)));
// (7) [1, 2, 3, 4, 5, 6, 7]
console.log(Array.from({ length: 7 }, (value, index) => index + 1));
// console.log(Array.from({ length: 7 }, (_, index) => index + 1));

////// querySelectorAll --> nodelist
////// dont have all array methods, like map()
////// convert to array

const movementsUI = Array.from(document.querySelectorAll('.movements__value'));
// (2) [div.movements__value, div.movements__value]
console.log(movementsUI);

const movementsUI2 = [...document.querySelectorAll('.movements__value')];

labelBalance.addEventListener('click', function () {
const movementsUI1 = Array.from(document.querySelectorAll('.movements__value'), el => Number(el.textContent.replace('€', '')));
// (8) [-30, 8500, -1000, -3210, -790, -150, 3400, 5000]
console.log(movementsUI1);

})

array summary

alt text

numbers and dates, times, intl

conveting and check numbers

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

// all floating 64
console.log(23 === 23.0); // 23
// binary base 2
console.log(0.1 + 0.2); // 0.30000000000000004

// convert string to number
console.log(Number('23'));
console.log(+'23');

///////// parsing

//(method) NumberConstructor.parseInt(string: string, radix?: number | undefined): number
// Converts A string to an integer.
// @param string — A string to convert into a number.
// @param radix
// A value between 2 and 36 that specifies the base of the number in string. If this argument is not supplied, strings with a prefix of '0x' are considered hexadecimal. All other strings are considered decimal.
console.log(Number.parseInt('30px')); // 30
console.log(Number.parseFloat(' 2.5rem ')); // 2.5

console.log(Number.isFinite(23)); // true
//
console.log(Number.isNaN(20)); // false
console.log(Number.isNaN('20')); // false
console.log(Number.isNaN(23 / 0)); // false
console.log(Number.isNaN(+'20x')); // true

// Only finite values of the type number, result in true.
// check if value is number
console.log(Number.isFinite(23)); // true
console.log(Number.isFinite(23 / 0)); // false
console.log(Number.isFinite(+'20x')); // false

console.log(Number.isInteger(23)); // true
console.log(Number.isInteger(23.2)); // false

Math method and rounding

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

// Returns the square root of a number.
console.log(Math.sqrt(25));
// console.log(25 ** (1 / 2));

// Returns the larger of a set of supplied numeric expressions.
console.log(Math.max(5, '454', 12, 35, 65));


console.log(Math.min(5, '454', 12, 35, 65));

console.log(Math.PI * 10 ** 2);

// random Returns a pseudorandom number between 0 and 1.
// trunc Returns the integral part of the a numeric expression, x, removing any fractional digits. If x is already an integer, the result is x.
console.log(Math.trunc(Math.random() * 6) + 1);

const randomInt = (min, max) => Math.trunc(Math.random() * (max - min) + 1) + min;
console.log(randomInt(2, 9));

///////// rounding integers

// Returns a supplied numeric expression rounded to the nearest integer.
console.log(Math.round(23.3)); // 23
console.log(Math.round(23.9)); // 24

// Returns the smallest integer greater than or equal to its numeric argument.
console.log(Math.ceil(23.3)); // 24
console.log(Math.ceil(23.9)); // 24
console.log(Math.ceil(23)); // 23
// Returns the greatest integer less than or equal to its numeric argument.
console.log(Math.floor(23.3)); // 23
console.log(Math.floor(23.9)); // 23
console.log(Math.floor(23)); // 23

console.log(Math.floor(-23.2)); // -24

// when positive value floor === trunc
console.log(Math.trunc(23.3)); // 23
console.log(Math.trunc(23.9)); // 23
console.log(Math.trunc(23)); // 23

console.log(Math.trunc(-23.2)); // -23


///// rounding decimals
console.log((2.7).toFixed(0)); // '3'
console.log((2.7).toFixed(2)); // '2.70'
console.log((2.2346).toFixed(2)); // '2.23'
console.log(+(2.2346).toFixed(2)); // 2.23

remainder operator

1
2
3
4
5
6
7
8
9

// remainder
console.log(5 % 2);

// even or odd

const isEven = n => n % 2 === 0;
console.log(isEven(8)); // true
console.log(isEven(9)); // false

numeric separators

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 2,874,600,000
const diameter = 2874600000;
const diameter1 = 2_874_600_000;
console.log(diameter1); // 2874600000

const priceCents = 345_99;
console.log(priceCents); // 34599

const PI = 3.14_15;
// const PI = 3._1415; // error
// const PI = 3_.1415; // error
// const PI = _3.1415; // error
// const PI = 3.1415_; // error

console.log(Number('230000'));
console.log(Number('230_000')); // NaN
console.log(Number.parseInt('230_000')); // 230

BigInt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

// biggest number javascipt can represent
// 9007199254740991
console.log(2 ** 53 - 1);
console.log(Number.MAX_SAFE_INTEGER);

// 1.2365469446566512e+32
console.log(123654694465665131313132131316464);

// 123654694465665131313132131316464n
console.log(123654694465665131313132131316464n);
console.log(BigInt(123654694465665131313132131316464n));

// Operations
// 1365746113224452954n
console.log(11321654n + 1365746113213131300n);

// Uncaught TypeError: Cannot mix BigInt and other types, use explicit conversions
// console.log(2315646 + 213654321316543123n);
console.log(BigInt(2315646) + 213654321316543123n);

console.log(typeof 20n); // 'bigint'

console.log(321654212354n + ' is big'); // 321654212354 is big


// divisions

console.log(10n / 3n); // 3n

creating dates

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
const now = new Date();
console.log(now);

console.log(new Date('Feb 21 2024 12:58:51'));

console.log(new Date('December 24, 2015'));

// '2019-11-18T21:31:17.178Z'
console.log(account1.movementsDates[0]);
console.log(new Date(account1.movementsDates[0]));

// Date(year: number, monthIndex: number, date?: number | undefined, hours?: number | undefined, minutes?: number | undefined, seconds?: number | undefined, ms?: number | undefined): Date
// The full year designation is required for cross-century date accuracy. If year is between 0 and 99 is used, then year is assumed to be 1900 + year.
// Thu Nov 19 2037 15:52:12 GMT+0800 (中国标准时间)
console.log(new Date(2037, 10, 19, 15, 52, 12));

// Thu Dec 03 2037 15:52:12 GMT+0800 (中国标准时间)
console.log(new Date(2037, 10, 33, 15, 52, 12));

///// time stamps
// 1970-01-01
console.log(new Date(0));
// 3 days
// Sun Jan 04 1970 08:00:00 GMT+0800 (中国标准时间)
console.log(new Date(3 * 24 * 60 * 60 * 1000));

/// working with Date

const future = new Date(2037, 10, 13, 15, 52)
// Fri Nov 13 2037 15:52:00 GMT+0800 (中国标准时间)
console.log(future);
console.log(future.getFullYear()) // 2037
console.log(future.getMonth()) // 10
console.log(future.getDate()) // 13
console.log(future.getHours()) // 15
console.log(future.getMinutes()) // 52
console.log(future.getSeconds()) // 0
console.log(future.getMilliseconds()) // 0

// 2037-11-13T07:52:00.000Z
console.log(future.toISOString()); //
// 2141711520000
console.log(future.getTime());
// Fri Nov 13 2037 15:52:00 GMT+0800 (中国标准时间)
console.log(new Date(2141711520000));
// 1708492446836
console.log(Date.now());

future.setFullYear(2040);
// Tue Nov 13 2040 15:52:00 GMT+0800 (中国标准时间)
console.log(future);

adding dates to bankist app project

1
2
const day = `${now.getDate()}`.padStart(2, 0);
const month = String(now.getMonth() + 1).padStart(2, 0);

operation with dates

1
2
3
4
5
6
7
8
9
10
11
12

const future = new Date(2037, 10, 13, 15, 52)
// 2141711520000
console.log(Number(future));

const daysPassed = function (date1, date2) {
return Math.abs(date2 - date1) / (24 * 60 * 60 * 1000);
}

const days1 = daysPassed(new Date(2037, 3, 14), new Date(2037, 3, 24, 10, 8))
// 10.422222222222222
console.log(days1);

internationalizing date (intl i18n)

ISO LANGUAGE CODE TABLE
http://www.lingoes.net/en/translator/langcode.htm

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

const now = new Date();
// new (locales?: string | string[] | undefined, options?: Intl.DateTimeFormatOptions | undefined) => Intl.DateTimeFormat
// (method) Intl.DateTimeFormat.format(date?: number | Date | undefined): string
// labelDate.textContent = new Intl.DateTimeFormat('en-GB').format(now);
// labelDate.textContent = new Intl.DateTimeFormat('en-US').format(now);
// labelDate.textContent = new Intl.DateTimeFormat('zh-CN').format(now);

const options = {
hour: 'numeric',
minute: 'numeric',
day: 'numeric',
// month: 'numeric',
// month: 'long',
month: '2-digit',
year: 'numeric',
weekday: 'long'


}
const locale = navigator.language;
// 'zh-CN'
console.log(locale);
labelDate.textContent = new Intl.DateTimeFormat('zh-CN', options).format(now);

internationalzing numbers (intl)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const num = 134641312.32;

const options = {
// style: 'unit',
// unit: 'mile-per-hour',
style: 'percent',
// style: 'currency',
// currency: 'EUR',
// useGrouping: false,

}

// US 134,641,312.32
console.log('US', new Intl.NumberFormat('en-US').format(num));
// Germany 134.641.312,32
console.log('Germany', new Intl.NumberFormat('de-DE').format(num));
// zh-CN 134,641,312.32 mi/h
const locale = navigator.language;
console.log(locale, new Intl.NumberFormat(locale, options).format(num));

timer: setTimeout setInterval

setTimeout once
setInterval repeat

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// setTimeout(handler: TimerHandler, timeout?: number | undefined, ...arguments: any[]): number
// 3000 -> 3s
// the code is not stop here
// Waiting...
// Here is your pizza
setTimeout(function () {
console.log('Here is your pizza');
}, 3000);
console.log('Waiting...');

// Here is your pizza with olives and spinach
setTimeout(function (ing1, ing2) {
console.log(`Here is your pizza with ${ing1} and ${ing2}`);
}, 3000, 'olives', 'spinach');

const ingredients = ['olives', 'spinach'];

const pizzaTimer = setTimeout(function (ing1, ing2) {
console.log(`Here is your pizza with ${ing1} and ${ing2}`);
}, 3000, ...ingredients);

console.log(pizzaTimer); // 4
if (ingredients.includes('spinach')) clearTimeout(pizzaTimer)
1
2
3
4
5
6
7
8
9
10
11
12
const timeInter = setInterval(function () {
const now = new Date();
const hour = now.getHours();
const min = now.getMinutes();
const sec = now.getSeconds();
console.log(`${hour}:${min}:${sec}`);
}, 1000);

setTimeout(function () {
clearInterval(timeInter);
}, 5000);

bankist project: implemetn a countdown

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
const startLogoutTimer = function () {
const tick = function () {
const min = String(Math.floor(time / 60)).padStart(2, 0);
const sec = String(time % 60).padStart(2, 0);

// print the remaining time to the ui
labelTimer.textContent = `${min}: ${sec}`;

// when 0 seconds, stop timer and logout user
if (time === 0) {
clearInterval(timer);
labelWelcome.textContent = 'LOG IN TO GET STARTED';
containerApp.style.opacity = 0;

}

time--;
}

// set time to 5 mins
let time = 120;

// call the timer every second
tick();
const timer = setInterval(tick, 1000);
return timer;
}
1
2
3
4
5
let timer;
btnLogin.addEventListener('click', function (e) {
if (timer) clearInterval(timer)
timer = startLogoutTimer();
});

advanced DOM and EVENT

DOM deep

  • allows us to make javascript interact with the browser;
  • we can write javascript to create, modify and delete Html elements; set styles, classes and attributes; and listen and respond to events;
  • dom tree is generated from an html document, which can then interac with;
  • dom is a very complex api(application programming interface) that contains lots of methods and properties to interact with the dom tree

types of dom objects

alt text

select, create, and delete elements

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

console.log(document.documentElement);
console.log(document.head);
console.log(document.body);

// Returns the first element that is a descendant of node that matches selectors.
const header = document.querySelector('.header');
// NodeList
const allSections = document.querySelectorAll('.section');
console.log(allSections);

console.log(document.getElementById('section--1'));
// HTMLCollection ---> live collection
console.log(document.getElementsByTagName('button'));
// HTMLCollection
console.log(document.getElementsByClassName('btn'));

// create and insert elements

// .insertAdjacentHTML()

// Creates an instance of the element for the specified tag.
const message = document.createElement('div');
// classList: Allows for manipulation of element's class content attribute as a set of whitespace-separated tokens through a DOMTokenList object.
message.classList.add('cookie-message');
message.textContent = 'We use cookied ....';

message.innerHTML = 'We use cookied ....<button class="btn btn--close-cookie">Got it!</button>';

// Inserts nodes before the first child of node, while replacing strings in nodes with equivalent Text nodes.
header.prepend(message);
// append: Inserts nodes after the last child of node, while replacing strings in nodes with equivalent Text nodes.
///// if it is used twice it is moved, not copy
// cloneNode: Returns a copy of node. If deep is true, the copy also includes the node's descendants.
header.append(message.cloneNode(true));

// Inserts nodes just after node, while replacing strings in nodes with equivalent Text nodes.
header.after(message);
// Inserts nodes just before node, while replacing strings in nodes with equivalent Text nodes.
header.before(message)

//////////delete elements

document.querySelector('.btn--close-cookie').addEventListener('click', function () {
// Removes node.
message.remove();
// message.parentElement.removeChild(message);
});

style, attribute and class

1
2
3
4
5
6
:root {
--color-primary: #5ec576;
--color-secondary: #ffcb03;
--color-tertiary: #ff585f;
--color-primary-darker: #4bbb7d;
}
1
2
3
4
5
6
7
8
9
<img
src="img/logo.png"
alt="Bankist logo"
class="nav__logo"
id="logo"
designer="lucfe"
data-version-number="3.0"
/>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71

//// inline style
message.style.backgroundColor = '#37383d';
message.style.width = '120%';
/// in the style sheet, no inline style ''
console.log(message.style.color);
// rgb(55, 56, 61)
console.log(message.style.backgroundColor);

// CSSStyleDeclaration {0: 'accent-color', 1: 'align-content',...}
// console.log(getComputedStyle(message));
console.log(getComputedStyle(message).color);

message.style.height = Number.parseFloat(getComputedStyle(message).height, 10) + 30 + 'px';
console.log(message.style.height); // 85.6667px

// setProperty:
// (method) CSSStyleDeclaration.setProperty(property: string, value: string | null, priority?: string | undefined): void
document.documentElement.style.setProperty('--color-primary', 'orangered')

////// Attributes

const logo = document.querySelector('.nav__logo');

///// element standard attributes/property
// Bankist logo
console.log(logo.alt);
/// full path
// http://127.0.0.1:5500/complete-javascript-course-master/13-Advanced-DOM-Bankist/starter/img/logo.png
console.log(logo.src);
// nav__logo
console.log(logo.className);

logo.alt = 'Beautiful minimalist logo'

//// non standard attributes/property

// Returns element's first attribute whose qualified name is qualifiedName,
console.log(logo.getAttribute('designer')); // lucfe
// relative path: img/logo.png
console.log(logo.getAttribute('src'));

// (method) Element.setAttribute(qualifiedName: string, value: string): void
// Sets the value of element's first attribute whose qualified name is qualifiedName to value.
logo.setAttribute('designer', 'jonas');

const link = document.querySelector('.nav__link--btn')
console.log(link.href);// http://127.0.0.1:5500/complete-javascript-course-master/13-Advanced-DOM-Bankist/starter/index.html#
console.log(link.getAttribute('href')); //

/// data attributes
// data-version-number="3.0"
console.log(logo.dataset.versionNumber); // 3.0


/// classList:
// (property) Element.classList: DOMTokenList
// Allows for manipulation of element's class content attribute as a set of whitespace-separated tokens through a DOMTokenList object.

// Adds all arguments passed, except those already present.
logo.classList.add('c', 'j');
// Adds all arguments passed, except those already present.
logo.classList.remove('c');
// If force is not given, "toggles" token, removing it if it's present and adding it if it's not present.
logo.classList.toggle('c');
// (method) DOMTokenList.contains(token: string): boolean
// Returns true if token is present, and false otherwise.
logo.classList.contains('c');

// !!! dont use/// erase all classes
// logo.className = 'jonas'

smooth scrolling

1
2
3
4
5
<div class="header__title">
<button class="btn--text btn--scroll-to">Learn more &DownArrow;</button>
</div>
<section class="section" id="section--1">
</section>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

const btnScrollTo = document.querySelector('.btn--scroll-to');
const section1 = document.getElementById('section--1');

btnScrollTo.addEventListener('click', e => {
const s1coords = section1.getBoundingClientRect();
// DOMRect {x: 0, y: 573.3333740234375, width: 1221.3333740234375, height: 1403.15625, top: 573.3333740234375, …}
console.log(s1coords);

console.log('current scroll (x/y)', window.pageXOffset, window.pageYOffset); // current scroll (x/y) 0 300

// height/width of the viewport 573 1221
console.log('height/width of the viewport', document.documentElement.clientHeight, document.documentElement.clientWidth);

// scrolling relative to the viewport
// window.scrollTo(s1coords.left + window.pageXOffset, s1coords.top + window.pageYOffset);

// // smoth
// window.scrollTo({
// letf: s1coords.left + window.scrollX,
// top: s1coords.top + window.scrollY,
// behavior: "smooth"
// });

/// modern browser
section1.scrollIntoView({
behavior: "smooth",
});
})

types of event and event handlers

old
<h1 onclick="alert('HTML alert')">

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

const h1 = document.querySelector('h1');

const alertH1 = function (e) {
alert('addEventListener: your are reading the heading');

// Removes the event listener in target's event listener list with the same type, callback, and options.
h1.removeEventListener('mouseenter', alertH1)
}

/// like mouse hover
/// can add multible eventhandlers
/// Appends an event listener for events whose type attribute value is type. The callback argument sets the callback that will be invoked when the event is dispatched.
h1.addEventListener('mouseenter', alertH1);

setTimeout(() => {
h1.removeEventListener('mouseenter', alertH1)
}, 5000);

// /// old school
// h1.onmouseenter = function (e) {
// alert('addEventListener: your are reading the heading')
// }

event propagation: bubbling and capturing

propagetion传播;培养
the spreading of something (a belief or practice) into new regions

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<html>
<head>
<title>A Simple Page</title>
</head>
<body>
<section>
<p>A paragraph with a <a href="#">link</a></p>
<p>A second paragraph</p>
</section>
<section>
<img src="dom.png" alt="THE DOM">
</section>
</body>
</html>
  1. capturing phase
  2. target phase
  3. bubbling phase

alt text

1
2
3
4
5
6
document.querySelector('section').addEventListener('click', () => {
alert('you clicked me')
});
document.querySelector('a').addEventListener('click', () => {
alert('you clicked me')
});

example:

alt text

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<nav class="nav">
<img
src="img/logo.png"
alt="Bankist logo"
class="nav__logo"
id="logo"
designer="lucfe"
data-version-number="3.0"
/>
<ul class="nav__links">
<li class="nav__item">
<a class="nav__link" href="#">Features</a>
</li>
</ul>
</nav>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

// document.querySelector('section').addEventListener('click', () => {
// alert('you clicked me')
// });
// document.querySelector('a').addEventListener('click', () => {
// alert('you clicked me')
// });


/// rgb(255, 255, 255)

const randomInt = (min, max) => Math.floor(Math.random() * (max - min + 1) + min);
const randomColor = () => `rgb(${randomInt(0, 255)}, ${randomInt(0, 255)}, ${randomInt(0, 255)})`

console.log(randomColor());

// nav__link
// nav__links
// nav

document.querySelector('.nav__link').addEventListener('click', function (e) {
console.log('nav__link');
this.style.backgroundColor = randomColor();

// <a> element
console.log('LINK', e.target);

// stop progagation
// When dispatched in a tree, invoking this method prevents event from reaching any objects other than the current object.
// e.stopPropagation();

// When set to true, options's capture prevents callback from being invoked when the event's eventPhase attribute value is BUBBLING_PHASE. When false (or not present), callback will not be invoked when event's eventPhase attribute value is CAPTURING_PHASE. Either way, callback will be invoked if event's eventPhase attribute value is AT_TARGET.
}, true)
document.querySelector('.nav__links').addEventListener('click', function (e) {
console.log('nav__links');
this.style.backgroundColor = randomColor();

// <a> element
console.log('LINKs', e.target);

/// this === e.currentTarget
// <ul>
console.log('Links', e.currentTarget);
})
document.querySelector('.nav').addEventListener('click', function (e) {
console.log('nav');
this.style.backgroundColor = randomColor();

// <a> element
console.log('nav', e.target);
})

event delegation implementing: page navigation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<nav class="nav">
<img
src="img/logo.png"
alt="Bankist logo"
class="nav__logo"
id="logo"
designer="lucfe"
data-version-number="3.0"
/>
<ul class="nav__links">
<li class="nav__item">
<a class="nav__link" href="#section--1">Features</a>
</li>
</ul>
<ul class="nav__links">
<li class="nav__item">
<a class="nav__link" href="#section--2">Features</a>
</li>
</ul>
</nav>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//////////// page navigation

// document.querySelectorAll('.nav__link').forEach(function (el) {
// el.addEventListener('click', function (e) {
// e.preventDefault();
// console.log('LINK');

// const id = this.getAttribute('href');
// console.log(id);
// document.querySelector(id).scrollIntoView({
// behavior: 'smooth'
// });
// });
// });

// 1. add event listenner to comon parent elemetnt
// 2. determine what element originated the event
document.querySelector('.nav__links').addEventListener('click', function (e) {
console.log(e.target);

e.preventDefault();

// mathing startegy
if (e.target.classList.contains('nav__link')) {
console.log('LINK');

const id = e.target.getAttribute('href');
console.log(id);
document.querySelector(id).scrollIntoView({
behavior: 'smooth'
});
}
});

examples 2
add eventlistener that dont exit at the monment

DOM traversingDOM traversing

select elmenent by another elements

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<div class="header__title">
<h1 onclick="alert('HTML alert')">
When
<!-- Green highlight effect -->
<span class="highlight">banking</span>
meets<br />
<span class="highlight">minimalist</span>
</h1>
<h4>A simpler banking experience for a simpler life.</h4>
<button class="btn--text btn--scroll-to">Learn more &DownArrow;</button>
<img
src="img/hero.png"
class="header__img"
alt="Minimalist bank items"
/>
</div>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

const h1 = document.querySelector('h1');

///// going downwards: child

///// no matter how deep the child element are
console.log(h1.querySelectorAll('.highlight'));

// NodeList(9) [text, comment, text, span.highlight, text, br, text, span.highlight, text]
console.log(h1.childNodes);
// HTMLCollection(3) [span.highlight, br, span.highlight]
console.log(h1.children);

// element: span.highlight
console.log(h1.firstElementChild);
// element: span.highlight
console.log(h1.lastElementChild);


///////// going upwards: parents


// element: div.header__title
console.log(h1.parentNode);
console.log(h1.parentElement);


// Returns the first (starting at element) inclusive ancestor that matches selectors, and null otherwise.
/// reverse directiong of querySelector
// element: header.header
console.log(h1.closest('.header'));
// element: h1
console.log(h1.closest('h1'));

// going sideways: siblings
// null
console.log(h1.previousElementSibling);
// element: h4
console.log(h1.nextElementSibling);


/// chaining
// HTMLCollection
console.log(h1.parentElement.children);
[...h1.parentElement.children].forEach(function (el) {
if (el !== h1) el.style.transform = 'scale(0.5)';
});

tabs: tabbed component

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<div class="operations">
<div class="operations__tab-container">
<button
class="btn operations__tab operations__tab--1 operations__tab--active"
data-tab="1"
>
<span>01</span>Instant Transfers
</button>
...
</div>


<div
class="operations__content operations__content--1 operations__content--active"
>
<div class="operations__icon operations__icon--1">
</div>
<h5 class="operations__header">
Tranfser money to anyone, instantly! No fees, no BS.
</h5>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim
ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
aliquip ex ea commodo consequat.
</p>
</div>
...
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

const tabs = document.querySelectorAll('.operations__tab');
const tabsContainer = document.querySelector('.operations__tab-container');
const tabsContent = document.querySelectorAll('.operations__content');

tabsContainer.addEventListener('click', e => {
e.preventDefault();
const clicked = e.target.closest('button');
console.log(clicked);

/// guard clause
if (!clicked) return;

/// remove active class and add active tab
tabs.forEach(t => t.classList.remove('operations__tab--active'));
clicked.classList.add('operations__tab--active');

/// active content area
tabsContent.forEach(content => { content.classList.remove('operations__content--active') });
document.querySelector(`.operations__content--${clicked.dataset.tab}`).classList.add('operations__content--active');

});

passing arguments to event handlers

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<nav class="nav">
<img
src="img/logo.png"
alt="Bankist logo"
class="nav__logo"
id="logo"
designer="lucfe"
data-version-number="3.0"
/>
<ul class="nav__links">
<li class="nav__item">
<a class="nav__link" href="#section--1">Features</a>
</li>
<li class="nav__item">
<a class="nav__link" href="#section--2">Operations</a>
</li>
<li class="nav__item">
<a class="nav__link" href="#section--3">Testimonials</a>
</li>
<li class="nav__item">
<a class="nav__link nav__link--btn btn--show-modal" href="#"
>Open account</a
>
</li>
</ul>
</nav>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

const handleHover = function (e, opacity) {
if (e.target.classList.contains('nav__link')) {
const link = e.target;
const siblings = link.closest('.nav').querySelectorAll('.nav__link');
const logo = link.closest('.nav').querySelector('img');
siblings.forEach(el => {
if (el !== link) {
el.style.opacity = this;
}
logo.style.opacity = this;
});
}
}

const nav = document.querySelector('.nav');
// mouseenter dont progonation

/// method 1
// nav.addEventListener('mouseover', function (e) {
// handleHover(e, 0.5);
// });

/// method 2
nav.addEventListener('mouseover', handleHover.bind(0.5)
);
// passing 'argument' into handler
// only one argument
nav.addEventListener('mouseout', handleHover.bind(1));

the scroll event: sticky navigation bar

1
2
3
4
.nav.sticky {
position: fixed;
background-color: rgba(255, 255, 255, 0.95);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/////// sticky naivgation

const initialCoords = section1.getBoundingClientRect();
console.log(initialCoords);

//// scroll event not RECOMMENTED
//// perfoming is bad
window.addEventListener('scroll', function (e) {
console.log(e);
console.log(window.scrollY);

if (window.scrollY > initialCoords.top) {
nav.classList.add('sticky');
} else {
nav.classList.remove('sticky');
}

})

better way: intersection observer api

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
///provides a way to asynchronously observe changes in the intersection of a target element with an ancestor element or with a top-level document's viewport.


// const obsCallback = function (entries, observer) {
// entries.forEach(entry => {
// console.log(entry); // IntersectionObserverEntry
// })
// };

// const obsOptions = {
// root: null,
// threshold: [0, 0.2],
// }


// const observer = new IntersectionObserver(obsCallback, obsOptions);
// observer.observe(section1);

const header = document.querySelector('.header');
const navHeight = nav.getBoundingClientRect().height;

const stickyNav = function (entries) {
const [entry] = entries;
console.log(entry);

if (!entry.isIntersecting) {
nav.classList.add('sticky')
} else {
nav.classList.remove('sticky')
}
}

const headerObserver = new IntersectionObserver(stickyNav, {
root: null,
threshold: 0,
rootMargin: `-${navHeight}px`
});

headerObserver.observe(header);

revealing elements on scroll

1
2
3
4
.section--hidden {
opacity: 0;
transform: translateY(8rem);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/////////// reveal sections

const allSections = document.querySelectorAll('.section');

const revealSection = function (entries, observer) {
const [entry] = entries;
console.log(entry);

if (!entry.isIntersecting) return;
entry.target.classList.remove('section--hidden');
observer.unobserve(entry.target);
};

const sectionObserver = new IntersectionObserver(revealSection, {
root: null,
threshold: 0.15,

});

allSections.forEach(section => {
section.classList.add('section--hidden');
sectionObserver.observe(section);
})

lazy image loading

1
2
3
4
5
6
<img
src="img/digital-lazy.jpg"
data-src="img/digital.jpg"
alt="Computer"
class="features__img lazy-img"
/>
1
2
3
.lazy-img {
filter: blur(20px);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
///// lazy loading images

const imgTargets = document.querySelectorAll('img[data-src]');
console.log(imgTargets);

const loadImg = function (entries, observer) {
const [entry] = entries;
console.log(entry);

if (!entry.isIntersecting) return;

// replace the src img with data-src
entry.target.src = entry.target.dataset.src;


// remove the filter css class
// should wait the image all loaded the remove the filter class
// entry.target.classList.remove('lazy-img');
entry.target.addEventListener('load', e => {
entry.target.classList.remove('lazy-img');
});


// remove the observer
observer.unobserve(entry.target);
}

const imgObserver = new IntersectionObserver(loadImg, {
root: null,
threshold: 0,
rootMargin: '200px',
});

imgTargets.forEach(img => imgObserver.observe(img));

building a slider component

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<section class="section" id="section--3">
<div class="section__title section__title--testimonials">TITLE</div>

<div class="slider">
<!-- <div class="slide slide--1">
<div class="testimonial">
Lorem ipsum dolor sit, amet consectetur adipisicing elit. Explicabo,
untur fugiat quae. Possimus illo est commodi iure
veritatis rem ducimus adipisci cupiditate, quae quisquam?
</div>
</div>

<div class="slide slide--2"></div> -->

<div class="slide"><img src="img/img-1.jpg" alt="Photo 1" /></div>
<div class="slide"><img src="img/img-2.jpg" alt="Photo 2" /></div>
<div class="slide"><img src="img/img-3.jpg" alt="Photo 3" /></div>
<div class="slide"><img src="img/img-4.jpg" alt="Photo 4" /></div>
<button class="slider__btn slider__btn--left">&larr;</button>
<button class="slider__btn slider__btn--right">&rarr;</button>
<div class="dots"></div>
</div>
</section>
1
2
3
4
5
<div class="dots">
<button class="dots__dot dots__dot--active" data-slide="0"></button>
<button class="dots__dot" data-slide="1"></button>
<button class="dots__dot" data-slide="2"></button>
</div>
1
2
3
4
.slider {

overflow: hidden;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
//////////////slider

const slider = function () {


const sildes = document.querySelectorAll('.slide');
const slider = document.querySelector('.slider');
const btnLeft = document.querySelector('.slider__btn--left');
const btnRight = document.querySelector('.slider__btn--right');
const dotContainer = document.querySelector('.dots');

let currentSlide = 0;
const maxSlide = sildes.length

// slider.style.overflow = 'visible';

const createDots = function () {
// for (let index = 0; index < maxSlide; index++) {
// const buttonEl = document.createElement('button');
// buttonEl.classList.add('dots__dot');
// buttonEl.dataset['slide'] = index;
// console.log(buttonEl);
// dotContainer.append(buttonEl);
// }
sildes.forEach((_, i) => {
dotContainer.insertAdjacentHTML('beforeend', `<button class="dots__dot" data-slide="${i}"></button>`);
});

};



const activateDot = function (slide) {
const dots = document.querySelectorAll('.dots__dot');
dots.forEach(dot => dot.classList.remove('dots__dot--active'));
const clicked = document.querySelector(`.dots__dot[data-slide="${slide}"]`)
clicked.classList.add('dots__dot--active');
}



const goToSlide = function (slide) {
sildes.forEach((s, i) => {
s.style.transform = `translateX(${100 * i - 100 * slide}%)`;
});

/// active dot
activateDot(slide);
}


/// next slide

const nextSlide = function () {
if (currentSlide === maxSlide - 1) {
currentSlide = 0;
} else {
currentSlide++;
}

/// -100 0 100 200
goToSlide(currentSlide);
}

const preSlide = function () {
if (currentSlide === 0) {
currentSlide = maxSlide - 1;
} else {
currentSlide--;
}

/// -100 0 100 200
goToSlide(currentSlide);
}

const init = function () {

createDots();
activateDot(0);
/// begining
goToSlide(0);
// 0, 100, 200, 300
// sildes.forEach((s, i) => {
// s.style.transform = `translateX(${100 * i}%)`;
// });
}

init();
/////////////events

btnRight.addEventListener('click', nextSlide);

btnLeft.addEventListener('click', preSlide)

document.addEventListener('keydown', function (e) {
// console.log(e);
if (e.key === 'ArrowLeft') {
preSlide();
} else if (e.key === 'ArrowRight') {
nextSlide();
}

});

dotContainer.addEventListener('click', function (e) {
e.preventDefault();
const clicked = e.target;
if (clicked.classList.contains('dots__dot')) {
goToSlide(clicked.dataset.slide);

/// active dot
activateDot(clicked.dataset.slide);

}
})

}

slider();

DOM load event

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// html and js downloaded
document.addEventListener('DOMContentLoaded', function (e) {
console.log(e);
console.log(e.timeStamp);
});

/// all resources loaded, include css, images
window.addEventListener('load', function (e) {
console.log(e);
});

/// before user leave the page, like: close the tab
// window.addEventListener('beforeunload', function (e) {
// e.preventDefault();
// console.log(e);
// e.returnValue = '';
// })

js loading and execude

alt text

alt text

OOP

what is OOP?

  • oop is a programming paradigm based on the concept of objects

  • we use objects to model(describe) real world or abstract features

  • object may contain data(properties) and code (methods). by using objects, we pack data and the corresponding behavior into one block;

  • in oop, objects are self-contained pieces/ blocks of code

  • objects are building blocks of applicatins, and interact with one another;

  • interactions happen through a pubblic interface (API): methods that the code outside of the object can access and use to communicate with the object;

  • OOP was developed with the goal of organizing code, to make it more flexible and easier to maintain(avoid ‘spaghetti code’)

  • Abstraction: Ignoring or hiding details that dont matter, allowing us to get an overview perspective of the thing we’re implementing, instead of messing with details that don’t really matter to our implementation.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
User {
user
private password
private email

login(word){
this.password == word
}
commnent(text) {
this.checkSPAM(text)
}
private checkSPAM(text){
// Verify logic
}
}
  • Encapsulation: Keeping properties and methos private inside the class, so they are not accessible from outside the class. Some methods cas be exposed as a public interface(API)

alt text

  • Inheritance: Making all properties and methods of a certain class available to a child class, forming a hierarchical relationshipp between classes. This allows us to reuse commmon logic and to model real world relationships.

alt text

  • Polymorphism: A child class can overwrite a method it inherited from a parent class.

javascript OOP

  • Objects are linked to a prototype object;
  • Prototypal inheritance: The prototype contains methods (behavior) that are accessible to all objects linked to that prototype;
  • behavior is delegated(授权) to the linked prototype object.

3 ways of implementing prototypal inheritance in javascript

  1. Constructor funtions
    • Technique to create objects from a function;
    • This is how built-in objects like Arrays, Maps or Sets are actually implemented.
  2. ES6 Classes
    • Modern alternative to constructor function syntax;
    • ‘Syntactic sugar’: behind the scenes, ES6 classes work exactly like constructor functions
    • ES6 clasees do not behavior like classes in “classical OOP”
  3. Object.create()
    • the easiest and most straightforward way of linking an object to a prototype object.

constructor function and the new operator

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//// dont use arrow function(no this keyword)
const Person = function (firstName, birthYear) {
// Person {}
console.log(this);

// instance properties
this.firstName = firstName;
this.birthYear = birthYear;

// /// dont do this//
// /// the object new create will all have the same copy function
// this.clacAge = function(){
// console.log(2037 - this.birthYear);
// }

}

const jonas = new Person('Jonas', 1991);
// Person {firstName: 'Jonas', birthYear: 1991}
console.log(jonas);

// 1. new object {} is created
// 2. function is called, this is point to the new object {}
// 3. the new object {} is linked to prototype
// 4. function automatically return the new object {}

const matilda = new Person('Matilda', 2017);
const jack = new Person('Jack', 1975);
console.log(matilda, jack);

const jay = 'Jay';

console.log(jonas instanceof Person); // true
console.log(jay instanceof Person); // false

prototypes

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// prototypes

//// all function have properties called prototype
/// all the objects (the constructor function created ) have all the methods and properties ( the prototype object have)


Person.prototype.calcAge = function () {
console.log(2037 - this.birthYear);
}
// {calcAge: ƒ, constructor: ƒ}
console.log(Person.prototype);

console.log(jonas);
jonas.calcAge(); // 46
matilda.calcAge(); // 20




// {calcAge: ƒ, constructor: ƒ}
console.log(jonas.__proto__);
console.log(jonas.__proto__ === Person.prototype); // true


console.log(Person.prototype.isPrototypeOf(jonas)); // ture
console.log(Person.prototype.isPrototypeOf(matilda)); // true
console.log(Person.prototype.isPrototypeOf(Person)); // false


///// set properties to prototype

Person.prototype.species = 'Homo Sapiens';

console.log(jonas.species, matilda.species);

console.log(jonas.hasOwnProperty('firstName')); // true
console.log(jonas.hasOwnProperty('species')); // false

prototypal inheritance: the prototype chain

alt text

alt text

prototypal inheritance: built-in objects

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58


// {species: 'Homo Sapiens', calcAge: ƒ, constructor: ƒ}
console.log(jonas.__proto__);
// {constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}
console.log(jonas.__proto__.__proto__);
/// null
console.log(jonas.__proto__.__proto__.__proto__);

// ƒ Person(firstName, birthYear)
console.dir(Person.prototype.constructor)

/// true
console.log(Person.prototype.constructor === Person)


///// array

const arr = [1, 2, 5, 47, 98, 74, 42, 2, 5];

// [constructor: ƒ, at: ƒ, concat: ƒ, copyWithin: ƒ, fill: ƒ, …]
console.log(arr.__proto__);
console.log(arr.__proto__ === Array.prototype); // true

// {constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}
console.log(arr.__proto__.__proto__);
console.log(arr.__proto__.__proto__ === Object.prototype); // true

////// example
////// dont do this in reality
Array.prototype.unique = function () {
return [...new Set(this)];
}
// (7) [1, 2, 5, 47, 98, 74, 42]
console.log(arr.unique());

//////

const h1 = document.querySelector('h1');
console.dir(h1);
// HTMLHeadingElement
console.dir(h1.__proto__);
// HTMLElement
console.dir(h1.__proto__.__proto__);
// Element
console.dir(h1.__proto__.__proto__.__proto__);
// Node
console.dir(h1.__proto__.__proto__.__proto__.__proto__);
// EventTarget
console.dir(h1.__proto__.__proto__.__proto__.__proto__.__proto__);
// Object
console.dir(h1.__proto__.__proto__.__proto__.__proto__.__proto__.__proto__);


/// ƒ anonymous()
console.dir((x => x + 1).__proto__);
/// Object
console.dir((x => x + 1).__proto__.__proto__);

ES6 classes

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//// class expression
// const PersonCl = class {}


//// class declaration
class PersonCl {
constructor(firstName, birthYear) {
this.firstName = firstName;
this.birthYear = birthYear;
}

/// Methods will be added to the Constructor function's prototype property
calcAge() {
console.log(2037 - this.birthYear);
}
}

const jessica = new PersonCl('Jessica', 1996);
console.log(jessica.firstName);

jessica.calcAge();

// true
console.log(jessica.__proto__ === PersonCl.prototype);


// 1. Classes are NOt hoisted
// 2. Class are first-class citizens
// 3. classes are executed in strict mode

classes: getter and setter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

class Car {
constructor(make, speed) {
this.make = make;
this.speed = speed;
}

accelerate() {
this.speed += 10;
console.log(this.speed);
}

brake() {
this.speed -= 5;
console.log(this.speed);
}

get speedUS() {
return this.speed / 1.6
}

set speedUS(speedUS) {
this.speed = speedUS * 1.6;
}
}

const bmw = new Car('BMW', 120);
const merc = new Car('Mercedes', 95);

const ford = new Car('Ford', 120);

console.log(ford.speed); // 120
console.log(ford.speedUS); // 75
console.log(ford.speedUS = 100); // 100
console.log(ford.speed); // 160

console.log(ford);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

const account = {
owner: 'jonas',
movements: [200, 530, 120, 300],


get latest() {
return this.movements.slice(-1).pop();
},

set latest(mov) {
this.movements.push(mov)
}
}

console.log(account.latest); // 300
account.latest = 50;
console.log(account.latest); // 50

console.log(account);

////// class

class PersonCl {
constructor(fullName, birthYear) {
this.fullName = fullName;
this.birthYear = birthYear;
}

/// Methods will be added to the Constructor function's prototype property
calcAge() {
console.log(2037 - this.birthYear);
}

/// add 'get age()' to prototype
//// add 'age' properties to object
get age() {
return 2037 - this.birthYear;
}

set fullName(name) {
if (name.includes(' ')) this._fullName = name;
else alert('not a full name')
}

get fullName() {
console.log('this come for getter');
return this._fullName;
}
}

const jessica = new PersonCl('Jessica Davis', 1996);

console.log(jessica);

console.log(jessica.age); // 41

console.log(jessica.fullName);
// 'not a full name'
// console.log(jessica.fullName = 'Jessica');
// 'not a full name'
// const walter = new PersonCl('Walter', 1996);

static methods

Array.from()
method belong to Constructor object

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54

const Person = function (firstName, birthYear) {
// Person {}
console.log(this);

// instance properties
this.firstName = firstName;
this.birthYear = birthYear;


}

const jonas = new Person('Jonas', 1991);
// Person {firstName: 'Jonas', birthYear: 1991}
console.log(jonas);

const matilda = new Person('Matilda', 2017);
const jack = new Person('Jack', 1975);
console.log(matilda, jack);

Person.hey = function () {
console.log('Hey there 👋');
console.dir(this);
}

// this: ƒ Person(firstName, birthYear)
/// object call a function, 'this' is the object
Person.hey();

// jonas.hey();

/////// class

class PersonCl {
constructor(fullName, birthYear) {
this.fullName = fullName;
this.birthYear = birthYear;
}

/// Methods will be added to the Constructor function's prototype property
calcAge() {
console.log(2037 - this.birthYear);
}

// not add to instance
static hey() {
console.log('Hey there 👋');
console.dir(this);
}
}


// this: class PersonCl
PersonCl.hey();

Object.create

alt text

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

const PersonProto = {
calcAge() {
console.log(2037 - this.birthYear);
},

init(firstName, birthYear) {
this.firstName = firstName;
this.birthYear = birthYear;
}
};

const steven = Object.create(PersonProto);

//
// {calcAge: ƒ}
console.log(steven.__proto__);
// true
console.log(steven.__proto__ === PersonProto);

steven.name = 'Steven';
steven.birthYear = 2002;

steven.calcAge(); // 35

/////
const sarah = Object.create(PersonProto);
sarah.init('Sarah', 1979);
sarah.calcAge();

inheritance between ‘classes’: constructor functions

alt text

alt text

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

const Person = function (firstName, birthYear) {
this.firstName = firstName;
this.birthYear = birthYear;

};

Person.prototype.calcAge = function () {
console.log(2037 - this.birthYear);
};


const Student = function (firstName, birthYear, course) {
// this.firstName = firstName;
// this.birthYear = birthYear;
Person.call(this, firstName, birthYear);

this.course = course;
};
///// Student.prototype.__proto__ === Person.prototype
Student.prototype = Object.create(Person.prototype);

////
// ƒ Person(firstName, birthYear)
console.dir(Student.prototype.constructor);
///////////
Student.prototype.constructor = Student;

Student.prototype.introduce = function () {
console.log(`My name is ${this.firstName} and I study ${this.course}`);
}

const mike = new Student('Mike', 2020, 'Computer Science');

console.log(mike);
mike.introduce();

mike.calcAge();



// {introduce: ƒ}
console.log(mike.__proto__);
// {calcAge: ƒ, constructor: ƒ}
console.log(mike.__proto__.__proto__);
// {constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}
console.log(mike.__proto__.__proto__.__proto__);

console.log(mike instanceof Student); // true
console.log(mike instanceof Person); // true
console.log(mike instanceof Object); // true

polymorphism

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

const Car = function (make, speed) {
this.make = make;
this.speed = speed;

}

Car.prototype.accelerate = function () {
this.speed += 10;
console.log(this.speed);
}
Car.prototype.brake = function () {
this.speed -= 5;
console.log(this.speed);
}

const car1 = new Car('BMW', 120);
const car2 = new Car('Mercedes', 95);

const EV = function (make, speed, charge) {
Car.call(this, make, speed);
this.charge = charge;
}

EV.prototype = Object.create(Car.prototype);

EV.prototype.constructor = EV;

EV.prototype.chargeBattery = function (chargeTo) {
this.charge = chargeTo;
};

EV.prototype.accelerate = function () {
this.speed += 20;
this.charge--;
console.log(`${this.make} going at ${this.speed} km/h, with a charge of ${this.charge}%`);
}

const tesla = new EV('Tesla', 120, 23);


tesla.accelerate();
tesla.chargeBattery(90);
tesla.accelerate();
tesla.accelerate();
tesla.chargeBattery(90);

inheritance between ‘classes’: ES6 classes

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

class PersonCl {
constructor(firstName, birthYear) {
this.firstName = firstName;
this.birthYear = birthYear;
}

/// Methods will be added to the Constructor function's prototype property
calcAge() {
console.log(2037 - this.birthYear);
}
}

const jessica = new PersonCl('Jessica', 1996);
console.log(jessica.firstName);

class StudentCl extends PersonCl {
constructor(firstName, birthYear, course) {
/// Always need to happen first!!
/// will create 'this' variable
super(firstName, birthYear);

this.course = course;
}
introduce() {
console.log(`My name is ${this.firstName} and I study ${this.course}`);
}

calcAge() {
console.log(`I feel more like ${2037 - this.birthYear + 10}`);
}
}

const martha = new StudentCl('Martha Jones', 2012, 'Computer Science');
// StudentCl {firstName: 'Martha Jones', birthYear: 2012, course: 'Computer Science'}
console.log(martha);
martha.introduce();

martha.calcAge();


///// if no added argument to super() method then :;
// class StudentCl1 extends PersonCl {
// }

// const martha1 = new StudentCl1('Martha Jones', 2012);
// /// StudentCl1 {firstName: 'Martha Jones', birthYear: 2012}
// console.log(martha1);

inheritance between ‘classes’: Object.create

alt text

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

const PersonProto = {
calcAge() {
console.log(2037 - this.birthYear);
},

init(firstName, birthYear) {
this.firstName = firstName;
this.birthYear = birthYear;
}
};

// const steven = Object.create(PersonProto);

const StudentProto = Object.create(PersonProto);
StudentProto.init = function (firstName, birthYear, course) {
PersonProto.init.call(this, firstName, birthYear);
this.course = course;
}
StudentProto.introduce = function () {
console.log(`My name is ${this.firstName} and I study ${this.course}`);
}

const jay = Object.create(StudentProto);

jay.init('Jay', 2010, 'Computer Science');
jay.calcAge();
jay.introduce();

class example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

class Account {
constructor(owner, currency, pin) {
this.owner = owner;
this.currency = currency;
this.pin = pin;

this.movements = [];
this.locale = navigator.language;

console.log(`Thanks for opening an account, ${this.owner}`);
}

deposit(val) {
this.movements.push(val);
}

withdraw(val) {
this.movements.push(-val);
}

approveLoan(val) {
return true;
}

requestLoan(val) {
if (this.approveLoan()) {
this.deposit(val);
console.log('Loan approved');
}
}
}

const acc1 = new Account('Jonas', 'EUR', 1111);
console.log(acc1);

// acc1.movements.push(250);
// acc1.movements.push(-140);
// acc1.approveLoan(1000)


acc1.deposit(250);
acc1.withdraw(140);
acc1.requestLoan(1000);

console.log(acc1);

Encapsulation: protected properties and methods

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

class Account {
constructor(owner, currency, pin) {
this.owner = owner;
this.currency = currency;
this._pin = pin;

this._movements = [];
this.locale = navigator.language;

console.log(`Thanks for opening an account, ${this.owner}`);
}

// public interface
getMovements() {
return this._movements;
}

deposit(val) {
this._movements.push(val);
}

withdraw(val) {
this._movements.push(-val);
}

_approveLoan(val) {
return true;
}

requestLoan(val) {
if (this._approveLoan(val)) {
this.deposit(val);
console.log('Loan approved');
}
}
}

const acc1 = new Account('Jonas', 'EUR', 1111);
console.log(acc1);

// acc1._movements.push(250);
// acc1._movements.push(-140);
// acc1._approveLoan(1000)

console.log(acc1.getMovements());


acc1.deposit(250);
acc1.withdraw(140);
acc1.requestLoan(1000);

console.log(acc1);

Encapsulation: private class fields and methods

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82

////////// not implement yet!!!!!!
// public fields
// private fields
// public Methods
// private Methods


class Account {
/// public fields
/// add to all instance of class, !!!!not add to the prototype
locale = navigator.language;

//// private fields
/// add to all instance of class, !!!!not add to the prototype
#movements = [];
#pin;


constructor(owner, currency, pin) {
this.owner = owner;
this.currency = currency;
/// protected property
this.#pin = pin;

// this._movements = [];
// this.locale = navigator.language;

console.log(`Thanks for opening an account, ${this.owner}`);
}

///// public Methods
// public interface
getMovements() {
return this.#movements;
}

deposit(val) {
this.#movements.push(val);
}

withdraw(val) {
this.#movements.push(-val);
}


requestLoan(val) {
if (this.#approveLoan(val)) {
this.deposit(val);
console.log('Loan approved');
}
}

/// Private methods
#approveLoan(val) {
return true;
}

}

const acc1 = new Account('Jonas', 'EUR', 1111);
console.log(acc1);

// acc1._movements.push(250);
// acc1._movements.push(-140);
// acc1._approveLoan(1000)

// Uncaught SyntaxError: Private field '#movements' must be declared in an enclosing class
// acc1.#movements.push(250);
// acc1.#movements.push(-140);
// Uncaught SyntaxError: Private field '#approveLoan' must be declared in an enclosing class
// acc1.#approveLoan(1000)

console.log(acc1.getMovements());


acc1.deposit(250);
acc1.withdraw(140);
acc1.requestLoan(1000);

console.log(acc1);

chain methods

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
class Account {
/// public fields
/// add to all instance of class, !!!!not add to the prototype
locale = navigator.language;

//// private fields
/// add to all instance of class, !!!!not add to the prototype
#movements = [];
#pin;


constructor(owner, currency, pin) {
this.owner = owner;
this.currency = currency;
/// protected property
this.#pin = pin;

// this._movements = [];
// this.locale = navigator.language;

console.log(`Thanks for opening an account, ${this.owner}`);
}

///// public Methods
// public interface
getMovements() {
return this.#movements;
}

deposit(val) {
this.#movements.push(val);
return this;
}

withdraw(val) {
this.#movements.push(-val);
return this
}


requestLoan(val) {
if (this.#approveLoan(val)) {
this.deposit(val);
console.log('Loan approved');
return this;
}
}

/// Private methods
#approveLoan(val) {
return true;
}

}

const acc1 = new Account('Jonas', 'EUR', 1111);
console.log(acc1);


console.log(acc1.getMovements());


acc1.deposit(250);
acc1.withdraw(140);
acc1.requestLoan(1000);

console.log(acc1);

/////////////////////////////////chaining

acc1.deposit(300).deposit(500).withdraw(35).requestLoan(3554).deposit(211);

console.log(acc1.getMovements());

OOP summary

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70

class Person {
constructor(fullName, birthYear) {
this.fullName = fullName;
this.birthYear = birthYear;
}
}
// Child class and parent class
// inheritance between classes, automatically sets prototype
class Student extends Person {
// public field(similar to property, available on created object)
university = 'University of London';
// private fields(not accessible outside of class)
#studyHours = 0;
#course;
// static public field (available only on class)
static numSubjects = 10;

// constructor method, called by new operator. mandatory(强制) in regular class, might be omitted in a child class
constructor(fullName, birthYear, startYear, course) {
// call to parent(super) class (necessary with extend).Need to happen before accessing this
super(fullName, birthYear);

// instance property(available on created object)
this.startYear = startYear;

// redefining private field
this.#course = course;
}

// public method
introduce() {
console.log(`I study ${this.#course} at ${this.university}`);
}

study(h) {
// referencing private field and method
this.#makecoffe();
this.#studyHours += h;
}

// private method(might not yet work in your browser. "fake" alternative: _ instead of #)
#makecoffe() {
return 'Here is a coffee for you '
}

// getter method
get testScore() {
return this._testScore;
}

// setter method(use _ to set property with same name as method, and also add getter)
set testScore(score) {
this._testScore = score <= 20 ? score : 0;
}

// static method(available only on class. can not access instance properties nor methods, only static ones)
static printCurriculum() {
console.log(`There are ${this.numSubjects} subjects`);
}
}

// creating new object with new operator
const studentJonas = new Student('Jonas', 2020, 2037, 'Medicine');
studentJonas.introduce(); // I study Medicine at University of London
studentJonas.study(2);
studentJonas.testScore = 36;
console.log(studentJonas.testScore); // 0
console.log(Student.numSubjects) // 10
Student.printCurriculum();

Mapty app OOP project

project planning

  1. user stories
    • description of the application’s functionality from the user’s perspective
    • common format: As a [type of user(who)], I want [an action(what)] so that [a benefit(why)]
    • example
      1. as a user, i want to log my running workouts with location, distance, time, pace and steps/minute, so i can keep a log of all my running
      2. as a user, i want to log my cycling workouts with location, distance, time, speed and elevtion gain, so i can keep a log of all my cycling
      3. as a user i want to see all my workouts at a glance, so i can easily track my progress over time
      4. as a user, i want to also see my workouts on a map, so i can easily check where i work out the most
      5. as a user, i want to see all my workouts when i leave the app and come back later, so that i can keep using the app over time
  2. features
    • user 1:
      • 1.map where user clicks to add new workout(best way to get location coordinates)
      • 2.geolocation to display map at current location(more user friendly)
      • 3.form to input distance, time, pace, steps/minute
    • user 2: 4.form to input distance, time, speed, elevation gain
    • user 3: 5.display all workouts in a list
    • user 4: 6.display all workouts on the map
    • user 5:
      • 7.store workout data in the browser using local storage API
      • 8.on page load, read the saved data from local storage and display
  3. flowchart
    alt text
  4. architecture
  5. development

the geolacation api

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

// Geolocation.getCurrentPosition(successCallback: PositionCallback, errorCallback?: PositionErrorCallback | null | undefined, options?: PositionOptions | undefined): void
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(
function (position) {
// console.log(position);
const { latitude, longitude } = position.coords;
console.log(latitude, longitude);
console.log(`https://www.google.com/maps/@${latitude},${longitude}`);
},
function () {
alert('Could not get your position')
});
}

leaflet library: displaying a map

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const coords = [latitude, longitude];

// L.map(map-container-html-element-id)setView([latitude, longitude], zoomratio)
const map = L.map('map').setView(coords, 13);

// L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
// attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
// }).addTo(map);
L.tileLayer('https://tile.openstreetmap.fr/hot/{z}/{x}/{y}.png', {
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);

L.marker(coords).addTo(map)
.bindPopup('A pretty CSS popup.<br> Easily customizable.')
.openPopup();

leaflet library: displaying a map marker

1
2
3
4
5
6
.running-popup .leaflet-popup-content-wrapper {
border-left: 5px solid var(--color-brand--2);
}
.cycling-popup .leaflet-popup-content-wrapper {
border-left: 5px solid var(--color-brand--1);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
map.on('click', function (mapEvent) {
console.log(mapEvent);
const { latlng: { lat, lng } } = mapEvent;
console.log(lat, lng);

L.marker([lat, lng]).addTo(map)
.bindPopup(L.popup({
maxWidth: 250,
minWidth: 100,
// the popup closing when another popup is opened.
autoClose: false,
// the popup closing when user clicks on the map
closeOnClick: false,
// A custom CSS class name to assign to the popup.
className: 'running-popup',
}))
// Sets the content of the popup bound to this layer.
.setPopupContent('Workout')
.openPopup();
});

render workout input form

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<form class="form hidden">
<div class="form__row">
<label class="form__label">Type</label>
<select class="form__input form__input--type">
<option value="running">Running</option>
<option value="cycling">Cycling</option>
</select>
</div>
<div class="form__row">
<label class="form__label">Distance</label>
<input class="form__input form__input--distance" placeholder="km" />
</div>
<div class="form__row">
<label class="form__label">Duration</label>
<input
class="form__input form__input--duration"
placeholder="min"
/>
</div>
<div class="form__row">
<label class="form__label">Cadence</label>
<input
class="form__input form__input--cadence"
placeholder="step/min"
/>
</div>
<div class="form__row form__row--hidden">
<label class="form__label">Elev Gain</label>
<input
class="form__input form__input--elevation"
placeholder="meters"
/>
</div>
<button class="form__btn">OK</button>
</form>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
const form = document.querySelector('.form');
const inputType = document.querySelector('.form__input--type');
const inputCadence = document.querySelector('.form__input--cadence');
const inputElevation = document.querySelector('.form__input--elevation');

let map, mapEvent;

if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(
function (position) {
// console.log(position);
const { latitude, longitude } = position.coords;
console.log(latitude, longitude);
console.log(`https://www.google.com/maps/@${latitude},${longitude}`);

const coords = [latitude, longitude];

// L.map(map-container-html-element-id)setView([latitude, longitude], zoomratio)
map = L.map('map').setView(coords, 13);

// L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
// attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
// }).addTo(map);
L.tileLayer('https://tile.openstreetmap.fr/hot/{z}/{x}/{y}.png', {
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);

// L.marker(coords).addTo(map)
// .bindPopup('A pretty CSS popup.<br> Easily customizable.')
// .openPopup();


/// handling clicks on map
map.on('click', function (mapE) {
mapEvent = mapE;

form.classList.remove('hidden');
inputDistance.focus();

});
},
function () {
alert('Could not get your position')
});
}


form.addEventListener('submit', function (e) {
e.preventDefault();

/// clear input field
inputDistance.value = inputDuration.value = inputCadence.value = inputElevation.value = '';

/// display marker
console.log(mapEvent);
const { latlng: { lat, lng } } = mapEvent;
console.log(lat, lng);
L.marker([lat, lng]).addTo(map)
.bindPopup(L.popup({
maxWidth: 250,
minWidth: 100,
// the popup closing when another popup is opened.
autoClose: false,
// the popup closing when user clicks on the map
closeOnClick: false,
// A custom CSS class name to assign to the popup.
className: 'running-popup',
}))
// Sets the content of the popup bound to this layer.
.setPopupContent('Workout')
.openPopup();
});

inputType.addEventListener('change', function () {
inputElevation.closest('.form__row').classList.toggle('form__row--hidden');
inputCadence.closest('.form__row').classList.toggle('form__row--hidden');
})

project architecture

alt text

refactoring the project code for architecture

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
// prettier-ignore
const months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];

const form = document.querySelector('.form');
const containerWorkouts = document.querySelector('.workouts');
const inputType = document.querySelector('.form__input--type');
const inputDistance = document.querySelector('.form__input--distance');
const inputDuration = document.querySelector('.form__input--duration');
const inputCadence = document.querySelector('.form__input--cadence');
const inputElevation = document.querySelector('.form__input--elevation');


class App {
#map;
#mapEvent;


constructor() {
this._getPostion();
form.addEventListener('submit', this._newWorkout.bind(this));
inputType.addEventListener('change', this._toggleElevationField);
}

_getPostion() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(
this._loadMap.bind(this),
function () {
alert('Could not get your position')
});
}
}

_loadMap(position) {
const { latitude, longitude } = position.coords;
const coords = [latitude, longitude];
this.#map = L.map('map').setView(coords, 13);

L.tileLayer('https://tile.openstreetmap.fr/hot/{z}/{x}/{y}.png', {
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}).addTo(this.#map);

/// handling clicks on map
this.#map.on('click', this._showForm.bind(this));
}


_showForm(mapE) {
this.#mapEvent = mapE;

form.classList.remove('hidden');
inputDistance.focus();
}

_newWorkout(e) {
e.preventDefault();

/// clear input field
inputDistance.value = inputDuration.value = inputCadence.value = inputElevation.value = '';

/// display marker
console.log(this.#mapEvent);
const { latlng: { lat, lng } } = this.#mapEvent;
console.log(lat, lng);
L.marker([lat, lng]).addTo(this.#map)
.bindPopup(L.popup({
maxWidth: 250,
minWidth: 100,
autoClose: false,
closeOnClick: false,
className: 'running-popup',
}))
.setPopupContent('Workout')
.openPopup();
}

_toggleElevationField() {
inputElevation.closest('.form__row').classList.toggle('form__row--hidden');
inputCadence.closest('.form__row').classList.toggle('form__row--hidden');
}

}

const app = new App();

creating classes to managing workout data

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

class Workout {
date = new Date();
id = (Date.now() + '').slice(-10);

constructor(coords, distance, duration) {
this.coords = coords; // [lati, long]
this.distance = distance; // in km
this.duration = duration; // in min

}
}

class Running extends Workout {
constructor(coords, distance, duration, cadence) {
super(coords, distance, duration);
this.cadence = cadence;
this.calcPace();
}

calcPace() {
// min / km
this.pace = this.duration / this.distance;
return this.pace;
}
}

class Cycling extends Workout {
constructor(coords, distance, duration, elevationGain) {
super(coords, distance, duration);
this.elevationGain = elevationGain;
this.calcSpeed();
}

calcSpeed() {
// min / km
this.speed = this.distance / (this.duration / 60);
return this.speed;
}
}

const run1 = new Running([39, -12], 5.2, 25, 178);
const cycling1 = new Cycling([39, -12], 27, 95, 532);

console.log(run1, cycling1);

create a new workout

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
class Running extends Workout {
type = 'running';}
class Cycling extends Workout {
type = 'cycling';}
class App {

#workouts = [];
_newWorkout(e) {

const validInputs = (...inputs) => inputs.every(inp => Number.isFinite(inp));
const allPositive = (...inputs) => inputs.every(inp => inp > 0);

e.preventDefault();

//// get data from form
const type = inputType.value;
// const distance = Number(inputDistance.value);
// const duration = Number(inputDuration.value);
const distance = +inputDistance.value;
const duration = +inputDuration.value;
const { latlng: { lat, lng } } = this.#mapEvent;
const coords = [lat, lng];
let workout;

//// if workout is runnign, create running object
if (type === 'running') {
const cadence = +inputCadence.value;
//// check the data is valid
// if (!Number.isFinite(distance) ||
// !Number.isFinite(duration) ||
// !Number.isFinite(cadence))
// return alert('input should be a number');
if (!validInputs(distance, duration, cadence) ||
!allPositive(distance, duration, cadence))
return alert('input should be a positive number');

workout = new Running(coords, distance, duration, cadence);
}

//// if workout is runnign, create running object
if (type === 'cycling') {
const elevation = +inputElevation.value;
if (!validInputs(distance, duration, elevation) ||
!allPositive(distance, duration))
return alert('input should be a positive number');

workout = new Cycling(coords, distance, duration, elevation);
}

//// ad new object to workout array
this.#workouts.push(workout);
console.log(this.#workouts);

//// render workout on map as marker
/// display marker
this._renderWorkoutMarker(workout);

//// render workout on list

//// hide form + clear input fields

/// clear input field
inputDistance.value = inputDuration.value = inputCadence.value = inputElevation.value = '';


}

_renderWorkoutMarker(workout) {
L.marker(workout.coords).addTo(this.#map)
.bindPopup(L.popup({
maxWidth: 250,
minWidth: 100,
autoClose: false,
closeOnClick: false,
className: `${workout.type}-popup`,
}))
.setPopupContent('workout')
.openPopup();
}

}

render workouts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<li class="workout workout--running" data-id="1234567890">
<h2 class="workout__title">Running on April 14</h2>
<div class="workout__details">
<span class="workout__icon">🏃‍♂️</span>
<span class="workout__value">5.2</span>
<span class="workout__unit">km</span>
</div>
<div class="workout__details">
<span class="workout__icon"></span>
<span class="workout__value">24</span>
<span class="workout__unit">min</span>
</div>
<div class="workout__details">
<span class="workout__icon">⚡️</span>
<span class="workout__value">4.6</span>
<span class="workout__unit">min/km</span>
</div>
<div class="workout__details">
<span class="workout__icon">🦶🏼</span>
<span class="workout__value">178</span>
<span class="workout__unit">spm</span>
</div>
</li>

<li class="workout workout--cycling" data-id="1234567891">
<h2 class="workout__title">Cycling on April 5</h2>
<div class="workout__details">
<span class="workout__icon">🚴‍♀️</span>
<span class="workout__value">27</span>
<span class="workout__unit">km</span>
</div>
<div class="workout__details">
<span class="workout__icon"></span>
<span class="workout__value">95</span>
<span class="workout__unit">min</span>
</div>
<div class="workout__details">
<span class="workout__icon">⚡️</span>
<span class="workout__value">16</span>
<span class="workout__unit">km/h</span>
</div>
<div class="workout__details">
<span class="workout__icon"></span>
<span class="workout__value">223</span>
<span class="workout__unit">m</span>
</div>
</li>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
_renderWorkout(workout) {
let html = `
<li class="workout workout--${workout.type}" data-id="${workout.id}">
<h2 class="workout__title">${workout.description}</h2>
<div class="workout__details">
<span class="workout__icon">${workout.type === 'running' ? '🏃‍♂️' : '🚴‍♀️'}</span>
<span class="workout__value">${workout.distance}</span>
<span class="workout__unit">km</span>
</div>
<div class="workout__details">
<span class="workout__icon">⏱</span>
<span class="workout__value">${workout.duration}</span>
<span class="workout__unit">min</span>
</div>
`;

if (workout.type === 'running') html += `
<div class="workout__details">
<span class="workout__icon">⚡️</span>
<span class="workout__value">${workout.pace.toFixed(1)}</span>
<span class="workout__unit">min/km</span>
</div>
<div class="workout__details">
<span class="workout__icon">🦶🏼</span>
<span class="workout__value">${workout.cadence}</span>
<span class="workout__unit">spm</span>
</div>
</li>
`;
if (workout.type === 'cycling') html += `
<div class="workout__details">
<span class="workout__icon">⚡️</span>
<span class="workout__value">${workout.speed.toFixed(1)}</span>
<span class="workout__unit">km/h</span>
</div>
<div class="workout__details">
<span class="workout__icon">⛰</span>
<span class="workout__value">${workout.elevationGain}</span>
<span class="workout__unit">m</span>
</div>
</li>
`;
form.insertAdjacentHTML('afterend', html);
}
_hideForm() {
/// clear input field
inputDistance.value = inputDuration.value = inputCadence.value = inputElevation.value = '';

form.style.display = 'none';
form.classList.add('hidden');
setTimeout(() => form.style.display = 'grid', 1000);
}

move to marker

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
_moveToPopup(e) {
const workoutEl = e.target.closest('.workout');
console.log(workoutEl);

if (!workoutEl) return;

const workout = this.#workouts.find(work => work.id === workoutEl.dataset.id);
console.log(workout);

this.#map.setView(workout.coords, this.#zoomLevel, {
animate: true,
pan: {
duration: 1,
},
});
}

local storage

JSON converting back to object dont have prototype chain

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
_loadMap(position) {

/// load local storage workout on the map
this.#workouts.forEach(work => {

this._renderWorkoutMarker(work);
});

}


_setLocalStorage() {

// localStorage.setItem()
// (method) Storage.setItem(key: string, value: string): void
// Sets the value of the pair identified by key to value, creating a new key/value pair if none existed for key previously.

// JSON.stringify()
// Converts a JavaScript value to a JavaScript Object Notation (JSON) string.
localStorage.setItem('workouts', JSON.stringify(this.#workouts));
}

_getLocalStorage() {
/// Converts a JavaScript Object Notation (JSON) string into an object.
const data = JSON.parse(localStorage.getItem('workouts'));
console.log(data);

if (!data) return;
this.#workouts = data;

// render workouts
this.#workouts.forEach(work => {
this._renderWorkout(work);
/// error: map havent load yet
// this._renderWorkoutMarker(work);
});
}

reset() {
localStorage.removeItem('workouts');
// Reloads the current page.
location.reload();
}

end

alt text

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
'use strict';


class Workout {
date = new Date();
id = (Date.now() + '').slice(-10);
clicks = 0;

constructor(coords, distance, duration) {
this.coords = coords; // [lati, long]
this.distance = distance; // in km
this.duration = duration; // in min

}

_setDescription() {
const months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];

this.description = `${this.type[0].toUpperCase()}${this.type.slice(1)} on ${months[this.date.getMonth()]} ${this.date.getDate()}`;
}

click() {
this.clicks++;
}
}

class Running extends Workout {
type = 'running';
constructor(coords, distance, duration, cadence) {
super(coords, distance, duration);
this.cadence = cadence;
this.calcPace();
this._setDescription();
}

calcPace() {
// min / km
this.pace = this.duration / this.distance;
return this.pace;
}
}

class Cycling extends Workout {
type = 'cycling';
constructor(coords, distance, duration, elevationGain) {
super(coords, distance, duration);
this.elevationGain = elevationGain;
this.calcSpeed();
this._setDescription();
}

calcSpeed() {
// min / km
this.speed = this.distance / (this.duration / 60);
return this.speed;
}
}



/////////////////////////

const form = document.querySelector('.form');
const containerWorkouts = document.querySelector('.workouts');
const inputType = document.querySelector('.form__input--type');
const inputDistance = document.querySelector('.form__input--distance');
const inputDuration = document.querySelector('.form__input--duration');
const inputCadence = document.querySelector('.form__input--cadence');
const inputElevation = document.querySelector('.form__input--elevation');

class App {
#map;
#mapEvent;
#workouts = [];
#zoomLevel = 13;

constructor() {
this._getPostion();

// get data from local storage
this._getLocalStorage();

// event handlers
form.addEventListener('submit', this._newWorkout.bind(this));
inputType.addEventListener('change', this._toggleElevationField);
containerWorkouts.addEventListener('click', this._moveToPopup.bind(this));
}

_getPostion() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(
this._loadMap.bind(this),
function () {
alert('Could not get your position')
});
}
}

_loadMap(position) {
const { latitude, longitude } = position.coords;
const coords = [latitude, longitude];
this.#map = L.map('map').setView(coords, this.#zoomLevel);

L.tileLayer('https://tile.openstreetmap.fr/hot/{z}/{x}/{y}.png', {
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}).addTo(this.#map);

/// handling clicks on map
this.#map.on('click', this._showForm.bind(this));

/// load local storage workout on the map
this.#workouts.forEach(work => {

this._renderWorkoutMarker(work);
});

}


_showForm(mapE) {
this.#mapEvent = mapE;

form.classList.remove('hidden');
inputDistance.focus();
}

_hideForm() {
/// clear input field
inputDistance.value = inputDuration.value = inputCadence.value = inputElevation.value = '';

form.style.display = 'none';
form.classList.add('hidden');
setTimeout(() => form.style.display = 'grid', 1000);
}

_newWorkout(e) {

const validInputs = (...inputs) => inputs.every(inp => Number.isFinite(inp));
const allPositive = (...inputs) => inputs.every(inp => inp > 0);

e.preventDefault();

//// get data from form
const type = inputType.value;

const distance = +inputDistance.value;
const duration = +inputDuration.value;
const { latlng: { lat, lng } } = this.#mapEvent;
const coords = [lat, lng];
let workout;

//// if workout is runnign, create running object
if (type === 'running') {
const cadence = +inputCadence.value;

if (!validInputs(distance, duration, cadence) ||
!allPositive(distance, duration, cadence))
return alert('input should be a positive number');

workout = new Running(coords, distance, duration, cadence);
}

//// if workout is runnign, create running object
if (type === 'cycling') {
const elevation = +inputElevation.value;
if (!validInputs(distance, duration, elevation) ||
!allPositive(distance, duration))
return alert('input should be a positive number');

workout = new Cycling(coords, distance, duration, elevation);
}

//// ad new object to workout array
this.#workouts.push(workout);
console.log(this.#workouts);

//// render workout on map as marker
/// display marker
this._renderWorkoutMarker(workout);

//// render workout on list
this._renderWorkout(workout);

//// hide form + clear input fields
this._hideForm();

/// set local storage to all workouts
this._setLocalStorage()

}

_toggleElevationField() {
inputElevation.closest('.form__row').classList.toggle('form__row--hidden');
inputCadence.closest('.form__row').classList.toggle('form__row--hidden');
}

_renderWorkoutMarker(workout) {
L.marker(workout.coords).addTo(this.#map)
.bindPopup(L.popup({
maxWidth: 250,
minWidth: 100,
autoClose: false,
closeOnClick: false,
className: `${workout.type}-popup`,
}))
.setPopupContent(`${workout.type === 'running' ? '🏃‍♂️' : '🚴‍♀️'} ${workout.description}`)
.openPopup();
}

_renderWorkout(workout) {
let html = `
<li class="workout workout--${workout.type}" data-id="${workout.id}">
<h2 class="workout__title">${workout.description}</h2>
<div class="workout__details">
<span class="workout__icon">${workout.type === 'running' ? '🏃‍♂️' : '🚴‍♀️'}</span>
<span class="workout__value">${workout.distance}</span>
<span class="workout__unit">km</span>
</div>
<div class="workout__details">
<span class="workout__icon">⏱</span>
<span class="workout__value">${workout.duration}</span>
<span class="workout__unit">min</span>
</div>
`;

if (workout.type === 'running') html += `
<div class="workout__details">
<span class="workout__icon">⚡️</span>
<span class="workout__value">${workout.pace.toFixed(1)}</span>
<span class="workout__unit">min/km</span>
</div>
<div class="workout__details">
<span class="workout__icon">🦶🏼</span>
<span class="workout__value">${workout.cadence}</span>
<span class="workout__unit">spm</span>
</div>
</li>
`;
if (workout.type === 'cycling') html += `
<div class="workout__details">
<span class="workout__icon">⚡️</span>
<span class="workout__value">${workout.speed.toFixed(1)}</span>
<span class="workout__unit">km/h</span>
</div>
<div class="workout__details">
<span class="workout__icon">⛰</span>
<span class="workout__value">${workout.elevationGain}</span>
<span class="workout__unit">m</span>
</div>
</li>
`;
form.insertAdjacentHTML('afterend', html);
}

_moveToPopup(e) {
const workoutEl = e.target.closest('.workout');
// console.log(workoutEl);

if (!workoutEl) return;

const workout = this.#workouts.find(work => work.id === workoutEl.dataset.id);
// console.log(workout);

this.#map.setView(workout.coords, this.#zoomLevel, {
animate: true,
pan: {
duration: 1,
},
});

/// using public interface
/// JSON converting back to object dont have prototype chain
console.log(workout);
// workout.click();
}

_setLocalStorage() {

localStorage.setItem('workouts', JSON.stringify(this.#workouts));
}

_getLocalStorage() {

const data = JSON.parse(localStorage.getItem('workouts'));
console.log(data);

if (!data) return;
this.#workouts = data;

// render workouts
this.#workouts.forEach(work => {
this._renderWorkout(work);
});
}

reset() {
localStorage.removeItem('workouts');
// Reloads the current page.
location.reload();
}
}

const app = new App();

asynchronous

ajax

synchronous

  • executed line by line
  • each line of code waits for previous line to finish
  • long-running operation block code execution

asynchronous

  • Asynchronous code is executed after a task that runs in the ‘background’ finishes;
  • Asynchronous code is non-blockig
  • Execution doesnt wait for an asynchronous task to finish its work
  • callback functions alone do nto make code asynchornous(e.g. [1, 2, 3].map(v => v * 2;))
  • EventListener alone do nto make code asynchornous
1
2
3
4
5
6
const img = document.querySelector('.dog');
img.src = 'dog.jpg'; // asynchronous
img.addEventListener('load', function(){
img.classList.add('fadeIn');
});
p.sytle.width = '300px';

Ajax:
Asychronous Javascript and xml:
allow us to communicate with remote web servers in an asynchronous way. with AJAx calls, we can request data from web servers dynamically.

API
Application Programming Interface
Piece of software that can be used by another piece of software, in order to allow applications to talk to each other;

Online API / Web API
Application running on a server, that receives requests for data, and send data back as response;
We can build our own web APIs(requires back-end development)

cors:
Cross-origin resource sharing (CORS) is a browser security feature that allows access to restricted resources on a web page from another domain.

xmlhttprequest

1
2
3
4
5
6
7
8
9
10
<article class="country">
<img class="country__img" src="" />
<div class="country__data">
<h3 class="country__name">COUNTRY</h3>
<h4 class="country__region">REGION</h4>
<p class="country__row"><span>👫</span>POP people</p>
<p class="country__row"><span>🗣️</span>LANG</p>
<p class="country__row"><span>💰</span>CUR</p>
</div>
</article>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
const btn = document.querySelector('.btn-country');
const countriesContainer = document.querySelector('.countries');

///////////////////////////////////////

const getCountryData = function (country) {
// Use XMLHttpRequest (XHR) objects to interact with servers. You can retrieve data from a URL without having to do a full page refresh. This enables a Web page to update just part of a page without disrupting what the user is doing.
const request = new XMLHttpRequest();

// open(method: string, url: string | URL): void
// Sets the request method, request URL, and synchronous flag.
request.open('GET', `https://restcountries.com/v3.1/name/${country}`);
request.send(); // asychronous

request.addEventListener('load', function () {
// console.log(this); /// request object
// console.log(this.responseText); // json
const [data] = JSON.parse(this.responseText);
// console.log(data);

const html = `
<article class="country">
<img class="country__img" src="${data.flags.png}" />
<div class="country__data">
<h3 class="country__name">${data.name.common}</h3>
<h4 class="country__region">${data.region}</h4>
<p class="country__row"><span>👫</span>${(+data.population / 1000000).toFixed(1)} people</p>
<p class="country__row"><span>🗣️</span>${Object.values(data.languages)[0]}</p>
<p class="country__row"><span>💰</span>${data.timezones[0]}</p>
</div>
</article>
`

countriesContainer.insertAdjacentHTML('beforeend', html);
countriesContainer.style.opacity = 1;
});
}

getCountryData('portugal');
getCountryData('usa');
getCountryData('china');

web: requests and response

TCP/IP socket connection
TCP: depack
IP: route

callback hell

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
const renderCountry = function (data, className = '') {
const html = `
<article class="country ${className}">
<img class="country__img" src="${data.flags.png}" />
<div class="country__data">
<h3 class="country__name">${data.name.common}</h3>
<h4 class="country__region">${data.region}</h4>
<p class="country__row"><span>👫</span>${(+data.population / 1000000).toFixed(1)} people</p>
<p class="country__row"><span>🗣️</span>${Object.values(data.languages)[0]}</p>
<p class="country__row"><span>💰</span>${data.timezones[0]}</p>
</div>
</article>
`

countriesContainer.insertAdjacentHTML('beforeend', html);
countriesContainer.style.opacity = 1;
}

const getCountryAndNeighborData = function (country) {

// Ajax call country 1
const request = new XMLHttpRequest();
request.open('GET', `https://restcountries.com/v3.1/name/${country}`);
request.send(); // asychronous

request.addEventListener('load', function () {
const [data] = JSON.parse(this.responseText);
console.log(data);

/// render country 1
renderCountry(data);

/// get neighbour country
const [neighbour] = data.borders;
console.log(neighbour);

if (!neighbour) return;

// Ajax call country 2
const request2 = new XMLHttpRequest();
request2.open('GET', `
https://restcountries.com/v3.1/alpha/${neighbour}`);
request2.send(); // asychronous

request2.addEventListener('load', function () {
const [data2] = JSON.parse(this.responseText);
console.log(data2);

/// render country 2
renderCountry(data2, 'neighbour');
})
});
}

getCountryAndNeighborData('portugal');
getCountryAndNeighborData('usa');
// getCountryAndNeighborData('china');

promises and the fetch API

less formal

promise:
an object that is used as a placeholder for the future result of an asynchronous operation
a container for an asynchronous delivered value
a container for a future value

  • we no longer need to rely on events and callbacks passed into asynchronous functions to handle asynchronous results;
  • instead of nesting callbakcs, we can chain promises for a sequence of asynchronous operation: escaping callback hell;

lifecycle:
pending –> settled (fulfilled, rejected)

build –> consume

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

// function fetch(input: RequestInfo | URL, init?: RequestInit | undefined): Promise<Response>
// const promise = fetch('https://restcountries.com/v3.1/name/portugal');

// console.log(promise);

const getCountryData = function (country) {

// (method) Promise<Response>.then<any, never>(onfulfilled?: ((value: Response) => any) | null | undefined, onrejected?: ((reason: any) => PromiseLike<never>) | null | undefined): Promise<any>
// Attaches callbacks for the resolution and/or rejection of the Promise
fetch(`https://restcountries.com/v3.1/name/${country}`).then(function (response) {
console.log(response); // Response object
//将输入作为 JSON 解析,以生成一个 JavaScript promise 对象。
return response.json()
}).then(function (data) {
console.log(data);
renderCountry(data[0])
});

}


getCountryData('portugal');

chaining promises

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
const getCountryData = function (country) {


fetch(`https://restcountries.com/v3.1/name/${country}`)
.then(function (response) {
return response.json()
})
.then(function (data) {
renderCountry(data[0]);
const neighbour = data[0].borders[0];
console.log(neighbour);

if (!neighbour) return;

return fetch(`https://restcountries.com/v3.1/alpha/${neighbour}`);

// ////!!! dont do this
// return fetch(`https://restcountries.com/v3.1/alpha/${neighbour}`).then().then();
})
.then(response => response.json())
.then(data => renderCountry(data[0], 'neighbour'));

}


getCountryData('portugal');

handling rejected promises

404 error is still the fullfill callback

1
2
3
4
5
6
fetch(`https://restcountries.com/v3.1/name/${country}`)
.then(function (response) {
return response.json()
}, function (err) {
console.log(err);
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
fetch(`https://restcountries.com/v3.1/name/${country}`)
.then(response => response.json())
.then(data => renderCountry(data[0], 'neighbour'))

//(method) Promise<void>.catch<void>(onrejected?: ((reason: any) => void | PromiseLike<void>) | null | undefined): Promise<void>
/// handling all the error from the first promise
.catch(err => {
console.log(err);
renderError(`Something went wrong ${err.message}`)
});


//(method) Promise<void>.finally(onfinally?: (() => void) | null | undefined): Promise<void>
// Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The resolved value cannot be modified from the callback.
.finally(() => {
countriesContainer.style.opacity = 1

});

throwing error manually

throwing error manually will be catch by the last catch() callback;

1
2
3
4
5
6
7
8
9
10
fetch(`https://restcountries.com/v3.1/name/${country}`)
.then(function (response) {
console.log(response);

if (!response.ok) {
throw new Error(`Country not found!${response.status}`)
}

return response.json()
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
const getJSON = function (url, errorMsg = 'Something went wrong') {
return fetch(url)
.then(function (response) {

if (!response.ok) {
throw new Error(`${errorMsg} ${response.status}`)
}

return response.json()
})
}

const getCountryData = function (country) {


getJSON(`https://restcountries.com/v3.1/name/${country}`, 'country not found')
.then(function (data) {
renderCountry(data[0]);

const neighbour = data[0].borders[0];

if (!neighbour) throw new Error('NO neighbour');

return getJSON(`https://restcountries.com/v3.1/alpha/${neighbour}`, 'country not found');
})
.then(data => renderCountry(data[0], 'neighbour'))

.catch(err => {
console.error(err);
console.dir(err);
renderError(`Something went wrong ${err.message}`)
})

.finally(() => {
countriesContainer.style.opacity = 1

});

}

event loop

promises
microtasks queue:
has priority over call back queue

eventlistener
callback queue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

console.log('Test start');

setTimeout(() => console.log('0 sec timer'), 0);
// (method) PromiseConstructor.resolve<string>(value: string): Promise<string> (+2 overloads)
// Creates a new resolved promise for the provided value.
Promise.resolve('Resolved promise 1').then(res => console.log(res));
Promise.resolve('Resolved promise 2').then(res => {
for (let index = 0; index < 100000; index++) {
}
console.log(res);
});
console.log('Test end');

// Test start
// Test end
// Resolved promise 1
// Resolved promise 2
// 0 sec timer

build a promise

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
/// var Promise: PromiseConstructor
// new <any>(executor: (resolve: (value: any) => void, reject: (reason?: any) => void) => void) => Promise<any>
// A callback used to initialize the promise. This callback is passed two arguments: a resolve callback used to resolve the promise with a value or the result of another promise, and a reject callback used to reject the promise with a provided reason or error.
const lotteryPromise = new Promise(function (resolve, reject) {
console.log('Lotter draw is happening');
setTimeout(function () {
if (Math.random() >= 0.5) {
resolve('You win 10000$');
} else {
reject(new Error('You lost your money'));
}
}, 2000);
});

lotteryPromise.then(res => console.log(res), err => console.log(err));


//// promisifying setTimeout
const wait = function (seconds) {
return new Promise(function (resolve) {
setTimeout(resolve, seconds * 1000);
})
}

wait(1)
.then(() => {
console.log(`1 second pasted`);
return wait(1);
})
.then(() => {
console.log(`2 second pasted`);
return wait(1);
})
.then(() => {
console.log(`3 second pasted`);
return wait(1);
})
.then(() => {
console.log(`4 second pasted`);
return wait(1);
})
.then(() => {
console.log(`5 second pasted`);
return wait(1);
})


setTimeout(() => {
console.log('1 second passed');
setTimeout(() => {
console.log('2 seconds passed');
setTimeout(() => {
console.log('3 seconds passed');
setTimeout(() => {
console.log('4 seconds passed');
setTimeout(() => {
console.log('5 seconds passed');
}, 1000);
}, 1000);
}, 1000);
}, 1000);
}, 1000);

Promise.resolve('abc').then(x => console.log(x));

promisify geolocation api

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

navigator.geolocation.getCurrentPosition(position => console.log(position), err => console.log(err));

// const getPostion = function () {
// return new Promise((resolve, reject) => {
// navigator.geolocation.getCurrentPosition(position => resolve(position), err => reject(err))
// });
// };
const getPostion = function () {
return new Promise((resolve, reject) => {
navigator.geolocation.getCurrentPosition(resolve, reject)
});
};

getPostion()
.then(position => {
console.log(position);
})
1
2
3
4
5
6
7
8
const whereAmI = function () {
getPostion()
.then(position => {
const { latitude: lat, longitude: lng } = position;
return fetch(`https://geocode.xyz/${lat},${lng}?geoit=json&auth=246057772253706376639x95870`)

});
}

code challenge 31

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
const wait = function (seconds) {
return new Promise(function (resolve) {
setTimeout(resolve, seconds * 1000);
})
}

const createImage = function (imgPath) {
return new Promise((resolve, reject) => {
const ImgEl = document.createElement('img');
ImgEl.src = imgPath;
ImgEl.addEventListener('load', () => {
document.querySelector('.images').append(ImgEl);
resolve(ImgEl);
});

ImgEl.addEventListener('error', function () {
reject(new Error('Image not found'));
});
});
};

let ImgEle;

createImage('img/img-1.jpg')
.then(ImgEl => {
ImgEle = ImgEl
return wait(2);
})
.then(() => {
ImgEle.style.display = 'none';
return createImage('img/img-2.jpg');
})
.then(ImgEl => {
ImgEle = ImgEl;
return wait(2)
})
.then(() => {
ImgEle.style.display = 'none';
})
.catch(err => console.log(err.message))

consuming promises with async/await

sugar cake

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
const getPostion = function () {
return new Promise((resolve, reject) => {
navigator.geolocation.getCurrentPosition(resolve, reject)
});
};

const whereAmI = async function () {
///geolocation
const position = await getPostion();
const { latitude: lat, longitude: lng } = position.coords

// reverse geocoding
const reGeo = await fetch(`https://geocode.xyz/${lat},${lng}?geoit=json&auth=246057772253706376639x95870`)
const dataGeo = await reGeo.json();
console.log(dataGeo);

// country data
const res = await fetch(`https://restcountries.com/v3.1/alpha/${dataGeo.prov}`);
console.log(res);
const data = await res.json();
console.log(data);
renderCountry(data[0]);
};

whereAmI();
console.log('First executed');

async/await error handling

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
const getPostion = function () {
return new Promise((resolve, reject) => {
navigator.geolocation.getCurrentPosition(resolve, reject)
});
};

const whereAmI = async function () {
try {///geolocation
const position = await getPostion();
const { latitude: lat, longitude: lng } = position.coords


// reverse geocoding
const reGeo = await fetch(`https://geocode.xyz/${lat},${lng}?geoit=json&auth=246057772253706376639x95870`)

if (!reGeo.ok) throw new Error('cant get location data')

const dataGeo = await reGeo.json();
console.log(dataGeo);


// country data
const res = await fetch(`https://restcountries.com/v3.1/alpha/${dataGeo.prov}`);
console.log(res);

if (!res.ok) throw new Error('cant get country data')

const data = await res.json();
console.log(data);
renderCountry(data[0]);

} catch (err) {
console.error(err);
renderError(`Something went wrong ${err.message}`)
}
};

whereAmI();
console.log('First executed');

//////////////////////////////

// try {
// let y = 1;
// const x = 2;
// x = 3;
// } catch (error) {
// // Assignment to constant variable.
// console.log(error.message);
// }

returning values from async functions

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
const getPostion = function () {
return new Promise((resolve, reject) => {
navigator.geolocation.getCurrentPosition(resolve, reject)
});
};

const whereAmI = async function () {
try {///geolocation
const position = await getPostion();
const { latitude: lat, longitude: lng } = position.coords


// reverse geocoding
const reGeo = await fetch(`https://geocode.xyz/${lat},${lng}?geoit=json&auth=246057772253706376639x95870`)

if (!reGeo.ok) throw new Error('cant get location data')

const dataGeo = await reGeo.json();

// country data
const res = await fetch(`https://restcountries.com/v3.1/alpha/${dataGeo.prov}`);

if (!res.ok) throw new Error('cant get country data')

const data = await res.json();
renderCountry(data[0]);

/// return the outfilled value !!!!!!
return dataGeo.prov;
} catch (err) {
console.error(err);
renderError(`Something went wrong ${err.message}`)

// reject promise returned from aysnc function
throw err;
}
};

console.log('1: will get location');
// const promise =
// whereAmI()
// .then(country => { console.log(country); })
// .catch(err => console.error(err.message))
// .finally(() => console.log('3: Finished getting location'));
// console.log('3: Finished getting location');

//// async await Ife

(async function () {
try {
console.log('1: will get location');
const country = await whereAmI();
console.log(country);

} catch (err) {
console.log(err);
}
console.log('3: Finished getting location');
})();

running promises in parallel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
const getJSON = function (url, errorMsg = 'Something went wrong') {
return fetch(url)
.then(function (response) {

if (!response.ok) {
throw new Error(`${errorMsg} ${response.status}`)
}

return response.json()
})
}

const get3Countries = async function (c1, c2, c3) {
try {

//// not parallel
// const [data1] = await getJSON(`https://restcountries.com/v3.1/name/${c1}`)
// const [data2] = await getJSON(`https://restcountries.com/v3.1/name/${c2}`)
// const [data3] = await getJSON(`https://restcountries.com/v3.1/name/${c3}`)

// (method) PromiseConstructor.all<[Promise<any>, Promise<any>, Promise<any>]>(values: [Promise<any>, Promise<any>, Promise<any>]): Promise<[any, any, any]> (+1 overload)
// Creates a Promise that is resolved with an array of results when all of the provided Promises resolve, or rejected when any Promise is rejected.
const data = await Promise.all([
getJSON(`https://restcountries.com/v3.1/name/${c1}`),
getJSON(`https://restcountries.com/v3.1/name/${c2}`),
getJSON(`https://restcountries.com/v3.1/name/${c3}`)]);

console.log(data.map(d => d[0].capital).flat());

// console.log(data1.capital, data2.capital, data2.capital);
} catch (error) {
console.error(error);
}
}

get3Countries('portugal', 'canada', 'tanza');

other promise combinators: race, allsettled and any

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

//// Promise.race
//// Creates a Promise that is resolved or rejected when any of the provided Promises are resolved or rejected.
(async function () {
const res = await Promise.race([
getJSON(`https://restcountries.com/v3.1/name/italy`),
getJSON(`https://restcountries.com/v3.1/name/egypt`),
getJSON(`https://restcountries.com/v3.1/name/mexico`)
]);
console.log(res);
})();

///example
const timeout = function (sec) {
return new Promise((_, reject) => {
setTimeout(() => {
reject(new Error('Request took too long!'));
}, sec * 1000);
});
};

(async function () {
const result = await Promise.race([
getJSON(`https://restcountries.com/v3.1/name/mexico`),
timeout(1),
])

console.log(result);
})();


/// Promise.allSettled
/// Creates a Promise that is resolved with an array of results when all of the provided Promises resolve or reject.
Promise.allSettled([
Promise.resolve('Sucess'),
Promise.resolve('Error'),
Promise.resolve('Anotrher result'),
]).then(res => console.log(res));


//// the first fullfilled promise
Promise.any([
Promise.resolve('Sucess'),
Promise.resolve('Error'),
Promise.resolve('Anotrher result'),
]).then(res => console.log(res));

Coding Challenge #3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
const wait = function (seconds) {
return new Promise(function (resolve) {
setTimeout(resolve, seconds * 1000);
})
}

const createImage = function (imgPath) {
return new Promise((resolve, reject) => {
const ImgEl = document.createElement('img');
ImgEl.src = imgPath;
ImgEl.addEventListener('load', () => {
document.querySelector('.images').append(ImgEl);
resolve(ImgEl);
});

ImgEl.addEventListener('error', function () {
reject(new Error('Image not found'));
});
});
};

const loadPause = async function () {
try {
const img = await createImage('img/img-1.jpg');
await wait(2);
img.style.display = 'none';
const img2 = await createImage('img/img-2.jpg');
await wait(2);
img2.style.display = 'none';
} catch (err) {
console.log(err);
}
}

// loadPause();

const loadAll = async function (imgArr) {
try {
const imgs = await Promise.all(imgArr.map(async img => await createImage(img)))
imgs.forEach(img => img.classList.add('parallel'))
} catch (err) {
console.error(err);
}
}
loadAll(['img/img-1.jpg', 'img/img-2.jpg', 'img/img-3.jpg']);