├── 05. HTML & CSS Crash Course.md ├── README.md ├── _The Complete JavaScript - Jonas Schmedtmann.md ├── 04. Developer Skills & Editor Setup.md ├── 09. A Closer Look at Functions.md ├── 12. Advanced DOM and Events.md ├── 01. JavaScript Fundamentals Part 1.md ├── 07. How JavaScript Works Behind the Scenes.md ├── 02. JavaScript Fundamentals Part 2.md ├── 11. Numbers, Dates, Intl and Timers.md ├── 13. Object-Oriented Programming (OOP) .md ├── 13. Object-Oriented Programming (O0P) With JavaScript.md └── 06. DOM and Events Fundamentals.md /05. HTML & CSS Crash Course.md: -------------------------------------------------------------------------------- 1 | # 5. HTML & CSS Crash Course 2 | --- 3 | 4 | ## Basic HTML Structure and Elements and Attributes, Classes and IDs 5 | ```html 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | Learning HTML & CSS 15 | 16 | 17 |

JavaScript is fun, but so is HTML & CSS!

18 |

19 | You can learn JavaScript without HTML and CSS, but for DOM manipulation 20 | it's useful to have some basic ideas of HTML & CSS. You can learn more 21 | about it 22 | on Udemy. 23 |

24 | 25 |

Another heading

26 |

27 | Just another paragraph 28 |

29 | 30 | 34 | 35 |
36 |

Your name here

37 |

Please fill in this form :)

38 | 39 | 40 | 41 |
42 | 43 | 44 | ``` 45 | 46 | ## Basic Styling with CSS & Introduction to the CSS Box Model 47 | 48 | ```css 49 | * { 50 | margin: 0; 51 | padding: 0; 52 | box-sizing: border-box; 53 | } 54 | 55 | body { 56 | background-color: rgb(255, 247, 201); 57 | font-family: Arial; 58 | font-size: 20px; 59 | padding: 50px; 60 | } 61 | 62 | h1 { 63 | font-size: 35px; 64 | margin-bottom: 25px; 65 | } 66 | 67 | h2 { 68 | margin-bottom: 20px; 69 | text-align: center; 70 | } 71 | 72 | p { 73 | margin-bottom: 20px; 74 | } 75 | 76 | .first { 77 | color: red; 78 | } 79 | 80 | ##your-name { 81 | background-color: rgb(255, 220, 105); 82 | border: 5px solid ##444; 83 | width: 400px; 84 | padding: 25px; 85 | margin-top: 30px; 86 | } 87 | 88 | input, 89 | button { 90 | padding: 10px; 91 | font-size: 16px; 92 | } 93 | 94 | a { 95 | background-color: yellowgreen; 96 | } 97 | 98 | ##course-image { 99 | width: 300px; 100 | } 101 | 102 | ##your-name h2 { 103 | color: olivedrab; 104 | } 105 | ``` -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The Complete Javascript Notes 2 | 3 | ### 🌟 What you'll learn 4 | 5 | - JavaScript fundamentals: variables, if/else, operators, boolean logic, functions, arrays, objects, loops, strings, etc. 6 | - Modern ES6+ from the beginning: arrow functions, destructuring, spread operator, optional chaining (ES2020), etc. 7 | - Modern OOP: Classes, constructors, prototypal inheritance, encapsulation, etc. 8 | - Complex concepts like the 'this' keyword, higher-order functions, closures, etc. 9 | - Asynchronous JavaScript: Event loop, promises, async/await, AJAX calls and APIs 10 | - How to architect your code using flowcharts and common patterns 11 | - Modern tools for 2024 and beyond: NPM, Parcel, Babel and ES6 modules! 12 | 13 | #### 📚 Topics Covered 14 | 15 | 01. [JavaScript Fundamentals Part 1](https://github.com/xoraus/Javascript-Notes/blob/main/01.%20JavaScript%20Fundamentals%20Part%201.md) 16 | 02. [JavaScript Fundamentals Part 2](https://github.com/xoraus/Javascript-Notes/blob/main/02.%20JavaScript%20Fundamentals%20Part%202.md) 17 | 03. How to Navigate This Course 18 | 04. [Developer Skills & Editor Setup](https://github.com/xoraus/Javascript-Notes/blob/main/04.%20Developer%20Skills%20%26%20Editor%20Setup.md) 19 | 05. [OPTIONAL: HTML & CSS Crash Course](https://github.com/xoraus/Javascript-Notes/blob/main/05.%20HTML%20%26%20CSS%20Crash%20Course.md) 20 | 06. [JavaScript in the Browser: DOM and Events Fundamentals](https://github.com/xoraus/Javascript-Notes/blob/main/06.%20DOM%20and%20Events%20Fundamentals.md) 21 | 07. [How JavaScript Works Behind the Scenes](https://github.com/xoraus/Javascript-Notes/blob/main/07.%20How%20JavaScript%20Works%20Behind%20the%20Scenes.md) 22 | 08. [Data Structures, Modern Operators and Strings](https://github.com/xoraus/Javascript-Notes/blob/main/08.%20Data%20Structures%2C%20Modern%20Operators%20and%20Strings.md) 23 | 09. [A Closer Look at Functions](https://github.com/xoraus/Javascript-Notes/blob/main/09.%20A%20Closer%20Look%20at%20Functions.md) 24 | 10. [Working With Arrays](https://github.com/xoraus/Javascript-Notes/blob/main/10.%20Working%20With%20Arrays.md) 25 | 11. [Numbers, Dates, Intl and Timers](https://github.com/xoraus/Javascript-Notes/blob/main/11.%20Numbers%2C%20Dates%2C%20Intl%20and%20Timers.md) 26 | 12. [Advanced DOM and Events](https://github.com/xoraus/Javascript-Notes/blob/main/12.%20Advanced%20DOM%20and%20Events.md) 27 | 13. [Object-Oriented Programming (O0P) With JavaScript](https://github.com/xoraus/Javascript-Notes/blob/main/13.%20Object-Oriented%20Programming%20(O0P)%20With%20JavaScript.md) 28 | 14. [Mapty App: OOP, Geolocation, External Libraries, and More!](https://github.com/xoraus/Javascript-Notes/blob/main/13.%20Object-Oriented%20Programming%20(OOP)%20.md) 29 | 15. [Asynchronous JavaScript: Promises, Async/Await, and AJAX](https://github.com/xoraus/Javascript-Notes/blob/main/14.%20Mapty%20App%20-%20OOP%2C%20Geolocation%2C%20External%20Libraries%2C%20and%20More!.md) 30 | 16. [Modern JavaScript Development: Modules, Tooling, and Functional]() 31 | 17. [Forkify App: Building a Modern Application]() 32 | 18. [Setting Up Git and Deployment]() 33 | 34 | #### 🎓 Certificate 35 | 36 | ![](https://udemy-certificate.s3.amazonaws.com/image/UC-a1144278-fed6-4c08-b13a-0e586af5fae7.jpg) 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /_The Complete JavaScript - Jonas Schmedtmann.md: -------------------------------------------------------------------------------- 1 | Up:: 2 | Tags:: #Project 3 | Status:: #Active 4 | Links:: [[Javascript MOC]] 5 | 6 | # The Complete JavaScript Course 7 | by Jonas Schmedtmann 8 | 9 | > ## Excerpt 10 | > The modern JavaScript course for everyone! Master JavaScript with projects, challenges and theory. Many courses in one! 11 | 12 | **Course Outcome** 13 | 14 | _By the end of the course, you will have the knowledge and confidence that you need in order to ace your job interviews and become a professional developer._ 15 | 16 | **So what exactly is covered in the course?** 17 | - Build 5 beautiful real-world projects for your portfolio! In these projects, you will learn how to plan and architect your applications using flowcharts and common JavaScript patterns 18 | - Master the JavaScript fundamentals: variables, if/else, operators, boolean logic, functions, arrays, objects, loops, strings, and more 19 | - Learn modern JavaScript (ES6+) from the beginning: arrow functions, destructuring, spread operator, default arguments, optional chaining (ES2020), and more 20 | - How JavaScript works behind the scenes: engines, the call stack, hoisting, scoping, the 'this' keyword, reference values, and more. 21 | - Deep dive into functions: arrow functions, first-class and higher-order functions, bind, and closures. 22 | - Deep dive into object-oriented programming: prototypal inheritance, constructor functions (ES5), classes (ES6), encapsulation, abstraction, inheritance, and polymorphism. \[This is like a small standalone course\] 23 | - Deep dive into asynchronous JavaScript: the event loop, promises, async/await, and error handling. You will use these to access data from third-party APIs with AJAX calls. \[This is like a small standalone course\] 24 | - Learn modern tools that are used by professional web developers: NPM, Parcel (module bundler), Babel, and ES6 modules 25 | 26 | # What you'll learn 27 | 28 | - Become an advanced, confident, and modern JavaScript developer from scratch 29 | - Build 6 beautiful real-world projects for your portfolio (not boring toy apps) 30 | - Become job-ready by understanding how JavaScript really works behind the scenes 31 | - How to think and work like a developer: problem-solving, researching, workflows 32 | - JavaScript fundamentals: variables, if/else, operators, boolean logic, functions, arrays, objects, loops, strings, etc. 33 | - Modern ES6+ from the beginning: arrow functions, destructuring, spread operator, optional chaining (ES2020), etc. 34 | - Modern OOP: Classes, constructors, prototypal inheritance, encapsulation, etc. 35 | - Complex concepts like the 'this' keyword, higher-order functions, closures, etc. 36 | - Asynchronous JavaScript: Event loop, promises, async/await, AJAX calls and APIs 37 | - How to architect your code using flowcharts and common patterns 38 | - Modern tools for 2022 and beyond: NPM, Parcel, Babel and ES6 modules 39 | - Practice your skills with 50+ challenges and assignments (solutions included) 40 | - Get fast and friendly support in the Q&A area 41 | - Course pathways: design your unique learning path according to your goals! 42 | 43 | # Cirriculumn 44 | - [[1. JavaScript Fundamentals Part 1]] 45 | - [[2. JavaScript Fundamentals Part 2]] 46 | - [[3. How to Navigate This Course]] 47 | - [[4. Developer Skills & Editor Setup]] 48 | - [[5. HTML & CSS Crash Course]] 49 | - [[6. JavaScript in the Browser - DOM and Events Fundamentals]] 50 | - [[7. How JavaScript Works Behind the Scenes]] 51 | - [[8. Data Structures, Modern Operators and Strings]] 52 | - [[9. A Closer Look at Functions]] 53 | - Working With Arrays 54 | - Numbers, Dates, Intl and Timers 55 | - Advanced DOM and Events 56 | - Object-Oriented Programming (O0P) With JavaScript 57 | - Mapty App: OOP, Geolocation, External Libraries, and More! 58 | - Asynchronous JavaScript: Promises, Async/Await, and AJAX 59 | - Modern JavaScript Development: Modules, Tooling, and Functional 60 | - Forkify App - Building a Modern Application 61 | - Setting Up Git and Deployment 62 | 63 | # Resources 64 | - https://codingheroes.io/resources/ 65 | 66 | npm install live-server -g 67 | 68 | -------------------------------------------------------------------------------- /04. Developer Skills & Editor Setup.md: -------------------------------------------------------------------------------- 1 | # 4. Developer Skills & Editor Setup 2 | --- 3 | ## How to fail at learning to Code 4 | 5 | A person named "_Noob Programmer_" 6 | 7 | - He didn't have a clear goal at the beginning of his journey 8 | - He started by watching courses and reading tutorials, but he would just copy the code without caring how it works. Sometimes he would just copy and paste code! 9 | - He didn't reinforce what he was learning by doing small challenges or taking notes He didn't practice coding, and didn't come up with his own project ideas 10 | - He quickly became frustrated when his code was not perfectly clean or efficient He lost motivation because he thought he could never know everything 11 | - He was learning in isolation 12 | - After finishing a couple of a courses, he thought he now was a web developer and could start applying to jobs. But he couldn't even build an app on his own! 13 | 14 | ## How to succeed at learning to Code 15 | 16 | - He didn’t have a clear goal at the beginning of his journey 17 | - **FIX** 18 | - Set a specific, measurable, realistic and time-based goal 19 | - Know exactly why you are learning to code: Switching careers? Finding a better job? 20 | - Imagine a big project you want to be able to build! 21 | - Research technologies you need and then learn them 22 | - He would just copy the code without caring how it works. Sometimes he would just copy and paste code! 23 | - **FIX** 24 | - Understand the code that you're studying and typing 25 | - Always type the code, don't copy-paste! 26 | - He didn't reinforce what he was learning by doing small challenges or taking notes 27 | - **FIX** 28 | - After you learn a new a feature or concept, use it immediately 29 | - Take notes 30 | - Challenge yourself and practice with small coding exercises and challenges 31 | - Don't be in a hurry to complete the course fast! 32 | - He didn't practice coding, and didn't come up with his own project ideas 33 | - **FIX** 34 | - Practicing on your own is the most important thing to do 35 | - This is NOT optional! Without practice outside of courses, you won't go anywhere! 36 | - Come up with your own project ideas or copy popular sites or applications, or just parts of them in the beginning Don't be stuck in "tutorial hell" 37 | - He quickly became frustrated when his code was not perfectly clean or efficient 38 | - **FIX** 39 | - Don't get stuck trying to write the perfect code! 40 | - Just write tons of code, no matter the quality! 41 | - Clean and efficient code will come with time 42 | - You can always refactor code later 43 | - He lost motivation because he thought he could never know everything 44 | - **FIX** 45 | - Embrace the fact that you will never you know everything 46 | - Just focus on what you need to achieve your goal! 47 | - He was learning in isolation 48 | - **FIX** 49 | - Explain new concepts to other people. If you can explain it, you truly understand it! 50 | - Share your goals to make yourself accountable 51 | - Share your learning progress with the web dev community ( ##100DaysOfCode, ##CodeNewbie, ##webdev, etc.) 52 | - After finishing a couple of courses, he thought he now was a web developer and could start applying to jobs 53 | - **FIX** 54 | - The biggest misconception that people have! 55 | - Courses are an amazing starting point, but are only the beginning of your journey! 56 | 57 | ## Learning how to code is hard, but you can do it! 58 | 59 | ![[4. Developer Skills & Editor Setup-1661521832385.jpeg]] 60 | 61 | ## How to fail at solving problems 62 | 63 | WHENEVER JOHN ENCOUNTERS A PROBLEM: 64 | * He jumps at the problem without much thinking 65 | * He implements his solution in an unstructured way 66 | * He gets stressed out when things don't work 67 | * He is too proud to research solutions 68 | - **FIX** 69 | - Stay calm and slow down, don't just jump at a problem without a plan 70 | - Take a very logical and rational approach (programming is just logic, in the end...) 71 | - Use my 4-step framework to solve any problem 72 | - Make sure you 100% understand the problem. Ask the right questions to get a clear picture of the problem 73 | - Divide and conquer: Break a big problem into smaller sub-problems. 74 | - Don't be afraid to do as much research as you have to 75 | - For bigger problems, write pseudo-code before writing the actual code 76 | 77 | ## What is a software bug? 78 | 79 | - Software bug: Defect or problem in a computer program. Basically, any unexpected or unintended behavior of a computer program is a software bug. 80 | - Bugs are completely normal in software development! 81 | - Debugging: Process of finding, fixing and preventing bugs. 82 | - _Note_ - A real bug which was causing an error in Harvard's computer in the 1940s 83 | 84 | ## The Debugging Process 85 | 86 | ![[4. Developer Skills & Editor Setup-1661522068743.jpeg]] 87 | 88 | -------------------------------------------------------------------------------- /09. A Closer Look at Functions.md: -------------------------------------------------------------------------------- 1 | # 9. A Closer Look at Functions 2 | --- 3 | ## Default Parameters 4 | ```js 5 | /////////////////////////////////////// 6 | // Default Parameters 7 | const bookings = []; 8 | 9 | const createBooking = function ( 10 | flightNum, 11 | numPassengers = 1, 12 | price = 199 * numPassengers 13 | ) { 14 | // ES5 15 | // numPassengers = numPassengers || 1; 16 | // price = price || 199; 17 | 18 | const booking = { 19 | flightNum, 20 | numPassengers, 21 | price, 22 | }; 23 | console.log(booking); 24 | bookings.push(booking); 25 | }; 26 | 27 | createBooking('LH123'); 28 | createBooking('LH123', 2, 800); 29 | createBooking('LH123', 2); 30 | createBooking('LH123', 5); 31 | 32 | createBooking('LH123', undefined, 1000); 33 | ``` 34 | - Default function parameters allow named parameters to be initialized with default values if no value or undefined is passed. 35 | - In JavaScript, function parameters default to [`undefined`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/undefined). However, it's often useful to set a different default value. 36 | - Sometimes, you can use the terms argument and parameter interchangeably. However, by definition, parameters are what you specify in the [function declaration](https://www.javascripttutorial.net/javascript-function/) whereas the arguments are what you pass into the function. 37 | 38 | ## How Passing Arguments Works: Value vs. Reference 39 | 40 | - Passing a primitive type to a function is really just the same as creating a copy like this, outside of the function. So the value is simply copied. On the other hand, when we pass an object to a function, it is really just like copying an object like this. And so whatever we change in a copy will also happen in the original. 41 | - In JavaScript, you can pass by value and by reference. The main difference between the two is that passing by value happens when assigning primitives while passing by reference when assigning objects. 42 | - In JavaScript primitive types are passed around as values: meaning that each time a value is assigned, a copy of that value is created. 43 | - On the other side objects (including plain objects, array, functions, class instances) are references. If you modify the object, then all variables that reference that object are going to see the change. 44 | - The comparison operator distinguishes comparing values and references. 2 variables holding references are equal only if they reference exactly the same object, but 2 variables holding values are equal if they simply have 2 same values no matter where the value originates: from a variable, literal, etc. 45 | ```js 46 | /////////////////////////////////////// 47 | // How Passing Arguments Works: Values vs. Reference 48 | const flight = 'LH234'; 49 | const jonas = { 50 | name: 'Jonas Schmedtmann', 51 | passport: 24739479284, 52 | }; 53 | 54 | const checkIn = function (flightNum, passenger) { 55 | flightNum = 'LH999'; 56 | passenger.name = 'Mr. ' + passenger.name; 57 | 58 | if (passenger.passport === 24739479284) { 59 | alert('Checked in'); 60 | } else { 61 | alert('Wrong passport!'); 62 | } 63 | }; 64 | 65 | // checkIn(flight, jonas); // this will modify the jonas object 66 | // console.log(flight); 67 | // console.log(jonas); 68 | 69 | // Is the same as doing... 70 | // const flightNum = flight; 71 | // const passenger = jonas; 72 | 73 | const newPassport = function (person) { 74 | person.passport = Math.trunc(Math.random() * 100000000000); 75 | }; 76 | 77 | newPassport(jonas); 78 | checkIn(flight, jonas); 79 | ``` 80 | 81 | ## First-Class and Higher-Order Functions 82 | - A programming language is said to have **First-class functions** when functions in that language are treated like any other variable. For example, in such a language, a function can be passed as an argument to other functions, can be returned by another function and can be assigned as a value to a variable. 83 | - **First-class functions** are JavaScript functions that can behave like variables. They can also be parsed as arguments to higher-order functions. 84 | - **Higher-order functions** are functions that return a function or take in a function as an argument. 85 | - So, first class functions is just a feature that a programming language either has or does not have. All it means is that all functions are values. There are no first class functions in practice, It's just a concept. 86 | - Any difference between First Class Function and High Order Function - [Stackoverflow](https://stackoverflow.com/questions/10141124/any-difference-between-first-class-function-and-high-order-function) 87 | - There is a difference. When you say that a language has first-class functions, it means that the language treats functions as values – that you can assign a function into a variable, pass it around etc. Higher-order functions are functions that work on other functions, meaning that they take one or more functions as an argument and can also return a function. 88 | - The “higher-order” concept can be applied to functions in general, like functions in the mathematical sense. The “first-class” concept only has to do with functions in programming languages. It’s seldom used when referring to a function, such as “a first-class function”. It’s much more common to say that “a language has/hasn’t first-class function support”. 89 | - The two things are closely related, as it’s hard to imagine a language with first-class functions that would not also support higher-order functions, and conversely a language with higher-order functions but without first-class function support. 90 | 91 | ![[9. A Closer Look at Functions-1662649740328.jpeg]] 92 | 93 | ## Functions Accepting Callback Functions 94 | ```js 95 | /////////////////////////////////////// 96 | // Functions Accepting Callback Functions 97 | const oneWord = function (str) { 98 | return str.replace(/ /g, '').toLowerCase(); 99 | }; 100 | 101 | const upperFirstWord = function (str) { 102 | const [first, ...others] = str.split(' '); 103 | return [first.toUpperCase(), ...others].join(' '); 104 | }; 105 | 106 | // Higher-order function 107 | const transformer = function (str, fn) { 108 | console.log(`Original string: ${str}`); 109 | console.log(`Transformed string: ${fn(str)}`); 110 | 111 | console.log(`Transformed by: ${fn.name}`); 112 | }; 113 | 114 | transformer('JavaScript is the best!', upperFirstWord); 115 | transformer('JavaScript is the best!', oneWord); 116 | 117 | // JS uses callbacks all the time 118 | const high5 = function () { 119 | console.log('👋'); 120 | }; 121 | document.body.addEventListener('click', high5); 122 | ['Jonas', 'Martha', 'Adam'].forEach(high5); 123 | ``` 124 | 125 | ## Functions Returning Functions 126 | 127 | ```js 128 | /////////////////////////////////////// 129 | // Functions Returning Functions 130 | const greet = function (greeting) { 131 | return function (name) { 132 | console.log(`${greeting} ${name}`); 133 | }; 134 | }; 135 | 136 | const greeterHey = greet('Hey'); 137 | greeterHey('Jonas'); 138 | greeterHey('Steven'); 139 | 140 | greet('Hello')('Jonas'); 141 | 142 | // Challenge 143 | const greetArr = greeting => name => console.log(`${greeting} ${name}`); 144 | 145 | greetArr('Hi')('Jonas'); 146 | 147 | ``` 148 | 149 | ## The call and apply Methods 150 | - The **`apply()/call()`** method calls the specified function with a given `this` value, and `arguments` provided as an array. 151 | - The `call()` method takes arguments **separately**. The `apply()` method takes arguments as an **array**. 152 | 153 | ```js 154 | /////////////////////////////////////// 155 | // The call and apply Methods 156 | const lufthansa = { 157 | airline: 'Lufthansa', 158 | iataCode: 'LH', 159 | bookings: [], 160 | // book: function() {} 161 | book(flightNum, name) { 162 | console.log( 163 | `${name} booked a seat on ${this.airline} flight ${this.iataCode}${flightNum}` 164 | ); 165 | this.bookings.push({ flight: `${this.iataCode}${flightNum}`, name }); 166 | }, 167 | }; 168 | 169 | lufthansa.book(239, 'Jonas Schmedtmann'); 170 | lufthansa.book(635, 'John Smith'); 171 | 172 | const eurowings = { 173 | airline: 'Eurowings', 174 | iataCode: 'EW', 175 | bookings: [], 176 | }; 177 | 178 | const book = lufthansa.book; 179 | 180 | // Does NOT work because this is pointing to 'Undefined' 181 | // book(23, 'Sarah Williams'); 182 | 183 | // Call method 184 | book.call(eurowings, 23, 'Sarah Williams'); 185 | console.log(eurowings); 186 | 187 | book.call(lufthansa, 239, 'Mary Cooper'); 188 | console.log(lufthansa); 189 | 190 | const swiss = { 191 | airline: 'Swiss Air Lines', 192 | iataCode: 'LX', 193 | bookings: [], 194 | }; 195 | 196 | book.call(swiss, 583, 'Mary Cooper'); 197 | 198 | // Apply method 199 | const flightData = [583, 'George Cooper']; 200 | book.apply(swiss, flightData); 201 | console.log(swiss); 202 | 203 | // Instead of using apply + array we can use call + ...rest 204 | book.call(swiss, ...flightData); 205 | ``` 206 | 207 | ## The bind Method 208 | - The **`bind()`** method creates a new function that, when called, has its `this` keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called. 209 | - In an event handler function, **this keyword** always points to the element on which that handler is attached to. 210 | - Partial applications means that we can preset parameters. 211 | - 212 | 213 | ```js 214 | /////////////////////////////////////// 215 | // The bind Method 216 | // book.call(eurowings, 23, 'Sarah Williams'); 217 | 218 | const bookEW = book.bind(eurowings); 219 | const bookLH = book.bind(lufthansa); 220 | const bookLX = book.bind(swiss); 221 | 222 | bookEW(23, 'Steven Williams'); 223 | 224 | const bookEW23 = book.bind(eurowings, 23); 225 | bookEW23('Jonas Schmedtmann'); 226 | bookEW23('Martha Cooper'); 227 | 228 | // With Event Listeners 229 | lufthansa.planes = 300; 230 | lufthansa.buyPlane = function () { 231 | console.log(this); 232 | 233 | this.planes++; 234 | console.log(this.planes); 235 | }; 236 | // lufthansa.buyPlane(); 237 | 238 | document 239 | .querySelector('.buy') 240 | .addEventListener('click', lufthansa.buyPlane.bind(lufthansa)); 241 | 242 | // Partial application 243 | const addTax = (rate, value) => value + value * rate; 244 | console.log(addTax(0.1, 200)); 245 | 246 | const addVAT = addTax.bind(null, 0.23); 247 | // addVAT = value => value + value * 0.23; 248 | 249 | console.log(addVAT(100)); 250 | console.log(addVAT(23)); 251 | 252 | const addTaxRate = function (rate) { 253 | return function (value) { 254 | return value + value * rate; 255 | }; 256 | }; 257 | const addVAT2 = addTaxRate(0.23); 258 | console.log(addVAT2(100)); 259 | console.log(addVAT2(23)); 260 | ``` 261 | 262 | ## Coding Challenge ##1 263 | Let's build a simple poll app! 264 | 265 | A poll has a question, an array of options from which people can choose, and an array with the number of replies for each option. This data is stored in the starter object below. 266 | 267 | Here are your tasks: 268 | 269 | 1. Create a method called 'registerNewAnswer' on the 'poll' object. The method does 2 things: 270 | - 1.1. Display a prompt window for the user to input the number of the selected option. The prompt should look like this: 271 | - What is your favourite programming language? 272 | - 0: JavaScript 273 | - 1: Python 274 | - 2: Rust 275 | - 3: C++ 276 | - (Write option number) 277 | - 1.2. Based on the input number, update the answers array. For example, if the option is 3, increase the value AT POSITION 3 of the array by 1. Make sure to check if the input is a number and if the number makes sense (e.g answer 52 wouldn't make sense, right?) 278 | 2. Call this method whenever the user clicks the "Answer poll" button. 279 | 3. Create a method 'displayResults' which displays the poll results. The method takes a string as an input (called 'type'), which can be either 'string' or 'array'. If type is 'array', simply display the results array as it is, using console.log(). This should be the default option. If type is 'string', display a string like "Poll results are 13, 2, 4, 1". 280 | 4. Run the 'displayResults' method at the end of each 'registerNewAnswer' method call. 281 | 282 | HINT: Use many of the tools you learned about in this and the last section 😉 283 | 284 | BONUS: Use the 'displayResults' method to display the 2 arrays in the test data. Use both the 'array' and the 'string' option. Do NOT put the arrays in the poll object! So what shoud the this keyword look like in this situation? 285 | 286 | BONUS TEST DATA 1: [5, 2, 3] 287 | BONUS TEST DATA 2: [1, 5, 3, 9, 6, 1] 288 | ```js 289 | const poll = { 290 | question: 'What is your favourite programming language?', 291 | options: ['0: JavaScript', '1: Python', '2: Rust', '3: C++'], 292 | // This generates [0, 0, 0, 0]. More in the next section 😃 293 | answers: new Array(4).fill(0), 294 | registerNewAnswer() { 295 | // Get answer 296 | const answer = Number( 297 | prompt( 298 | `${this.question}\n${this.options.join('\n')}\n(Write option number)` 299 | ) 300 | ); 301 | console.log(answer); 302 | 303 | // Register answer 304 | typeof answer === 'number' && 305 | answer < this.answers.length && 306 | this.answers[answer]++; 307 | 308 | this.displayResults(); 309 | this.displayResults('string'); 310 | }, 311 | 312 | displayResults(type = 'array') { 313 | if (type === 'array') { 314 | console.log(this.answers); 315 | } else if (type === 'string') { 316 | // Poll results are 13, 2, 4, 1 317 | console.log(`Poll results are ${this.answers.join(', ')}`); 318 | } 319 | }, 320 | }; 321 | 322 | document 323 | .querySelector('.poll') 324 | .addEventListener('click', poll.registerNewAnswer.bind(poll)); 325 | 326 | poll.displayResults.call({ answers: [5, 2, 3] }, 'string'); 327 | poll.displayResults.call({ answers: [1, 5, 3, 9, 6, 1] }, 'string'); 328 | poll.displayResults.call({ answers: [1, 5, 3, 9, 6, 1] }); 329 | 330 | // [5, 2, 3] 331 | // [1, 5, 3, 9, 6, 1] 332 | ``` 333 | 334 | ## Immediately Invoked Function Expressions (IIFE) 335 | - IIFEs are functions that are executed _immediately after_ being defined. We can make any function expression an IIFE by wrapping it in parentheses, and adding a following pair of parentheses at the end: 336 | ```js 337 | (function() { 338 | // Code that runs in your function 339 | })() 340 | ``` 341 | - Alternatively, you can use the arrow syntax to create an IIFE as follows: 342 | ```js 343 | (() => { 344 | // Code that runs in your function 345 | })() 346 | ``` 347 | - The parentheses surrounding the function definition lets JavaScript know that it will process a function expression. The last pair of parentheses invoke the function. 348 | ###### When to Use an IIFE? 349 | 350 | The most common use cases for IIFEs are: 351 | 352 | - Aliasing global variables 353 | - Creating private variables and functions 354 | - Asynchronous functions in loops 355 | ```js 356 | /////////////////////////////////////// 357 | // Immediately Invoked Function Expressions (IIFE) 358 | const runOnce = function () { 359 | console.log('This will never run again'); 360 | }; 361 | runOnce(); 362 | 363 | // IIFE 364 | (function () { 365 | console.log('This will never run again'); 366 | const isPrivate = 23; 367 | })(); 368 | 369 | // console.log(isPrivate); 370 | 371 | (() => console.log('This will ALSO never run again'))(); 372 | 373 | { 374 | const isPrivate = 23; 375 | var notPrivate = 46; 376 | } 377 | // console.log(isPrivate); 378 | console.log(notPrivate); 379 | ``` 380 | - [more on IIFE](https://stackabuse.com/javascripts-immediately-invoked-function-expressions/) 381 | 382 | ## Closures 383 | - A **closure** is a function which has access to the variable from another function’s scope. This is accomplished by creating a function inside a function. 384 | - A **closure** is the combination of a function bundled together (enclosed) with references to its surrounding state (the **lexical environment**). In other words, a closure gives you access to an outer function's scope from an inner function. In JavaScript, closures are created every time a function is created, at function creation time. 385 | - JavaScript variables can belong to the **local** or **global** scope. Global variables can be made local (private) with **closures**. 386 | - [Closures in Javascript for beginners](https://www.codingame.com/playgrounds/6516/closures-in-javascript-for-beginners 387 | - **Creating A Closure** 388 | - ![[9. A Closer Look at Functions-1662920198036.jpeg]] 389 | - Understanding Closure 390 | - ![[9. A Closer Look at Functions-1662920260588.jpeg]] 391 | - Summary 392 | - ![[9. A Closer Look at Functions-1662920308154.jpeg]] 393 | ```js 394 | /////////////////////////////////////// 395 | // Closures 396 | const secureBooking = function () { 397 | let passengerCount = 0; 398 | 399 | return function () { 400 | passengerCount++; 401 | console.log(`${passengerCount} passengers`); 402 | }; 403 | }; 404 | 405 | const booker = secureBooking(); 406 | 407 | booker(); 408 | booker(); 409 | booker(); 410 | 411 | console.dir(booker); 412 | ``` 413 | 414 | ## More Closure Examples 415 | - a closure always makes sure that a function does not lose the connection to the variables that were present at its birthplace. 416 | - closure does in fact have priority over the scope chain. 417 | ```js 418 | /////////////////////////////////////// 419 | // More Closure Examples 420 | // Example 1 421 | let f; 422 | 423 | const g = function () { 424 | const a = 23; 425 | f = function () { 426 | console.log(a * 2); 427 | }; 428 | }; 429 | 430 | const h = function () { 431 | const b = 777; 432 | f = function () { 433 | console.log(b * 2); 434 | }; 435 | }; 436 | 437 | g(); 438 | f(); // output: 46 439 | console.dir(f); 440 | 441 | // Re-assigning f function 442 | h(); 443 | f(); 444 | console.dir(f); 445 | 446 | // Example 2 447 | const boardPassengers = function (n, wait) { 448 | const perGroup = n / 3; 449 | 450 | setTimeout(function () { 451 | console.log(`We are now boarding all ${n} passengers`); 452 | console.log(`There are 3 groups, each with ${perGroup} passengers`); 453 | }, wait * 1000); 454 | 455 | console.log(`Will start boarding in ${wait} seconds`); 456 | }; 457 | 458 | const perGroup = 1000; 459 | boardPassengers(180, 3); 460 | */ 461 | ``` 462 | 463 | ## Coding Challenge ##2 464 | This is more of a thinking challenge than a coding challenge 🤓 465 | 466 | Take the IIFE below and at the end of the function, attach an event listener that changes the color of the selected h1 element ('header') to blue, each time the BODY element is clicked. Do NOT select the h1 element again! 467 | 468 | And now explain to YOURSELF (or someone around you) WHY this worked! Take all the time you need. Think about WHEN exactly the callback function is executed, and what that means for the variables involved in this example.e 469 | 470 | ```js 471 | (function () { 472 | const header = document.querySelector('h1'); 473 | header.style.color = 'red'; 474 | document.querySelector('body').addEventListener('click', function () { 475 | header.style.color = 'blue'; 476 | }); 477 | })(); 478 | 479 | ``` -------------------------------------------------------------------------------- /12. Advanced DOM and Events.md: -------------------------------------------------------------------------------- 1 | # 12. Advanced DOM and Events 2 | --- 3 | 4 | ## How the DOM Really Works 5 | ![[12. Advanced DOM and Events-1663411720931.jpeg]] 6 | 7 | - Allows us to make JavaScript interact with the browser; 8 | - 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 we can then interact with; 9 | - DOM is a very complex API that contains lots of methods and properties to interact with the DOM tree 10 | ```js 11 | .querySelector() / .addEventListener() / .createElement() / 12 | .innerHTML / .textContent / .children / etc ... 13 | ``` 14 | 15 | **How the DOM API is organized behind the Scenes** 16 | ![[12. Advanced DOM and Events-1663412092906.jpeg]] 17 | 18 | - A DOM API is broken up into these different types of nodes. also want you to understand that each of these types of nodes has access to different properties and methods and that some of them even inherit more properties and methods from their ancestors in this organization. 19 | 20 | ## Selecting, Creating, and Deleting Elements 21 | ```js 22 | /////////////////////////////////////// 23 | // Selecting, Creating, and Deleting Elements 24 | 25 | // Selecting elements 26 | console.log(document.documentElement); 27 | console.log(document.head); 28 | console.log(document.body); 29 | 30 | const header = document.querySelector('.header'); 31 | const allSections = document.querySelectorAll('.section'); 32 | console.log(allSections); 33 | 34 | document.getElementById('section--1'); 35 | const allButtons = document.getElementsByTagName('button'); 36 | console.log(allButtons); 37 | 38 | console.log(document.getElementsByClassName('btn')); 39 | 40 | // Creating and inserting elements 41 | const message = document.createElement('div'); 42 | message.classList.add('cookie-message'); 43 | // message.textContent = 'We use cookied for improved functionality and analytics.'; 44 | message.innerHTML = 45 | 'We use cookied for improved functionality and analytics. '; 46 | 47 | // header.prepend(message); 48 | header.append(message); 49 | // header.append(message.cloneNode(true)); 50 | 51 | // header.before(message); 52 | // header.after(message); 53 | 54 | // Delete elements 55 | document 56 | .querySelector('.btn--close-cookie') 57 | .addEventListener('click', function () { 58 | // message.remove(); // new way of removing element 59 | message.parentElement.removeChild(message); // old way of removing element 60 | }); 61 | ``` 62 | 63 | ## Styles, Attributes and Classes 64 | ```js 65 | message.style.background = '#37383d'; 66 | message.style.width = '120%'; 67 | 68 | console.log(message.style.color); 69 | console.log(message.style.backgroundColor); 70 | 71 | console.log(getComputedStyle(message).color); 72 | console.log(getComputedStyle(message).height); 73 | 74 | message.style.height = 75 | Number.parseFloat(getComputedStyle(message).height, 10) + 30 + 'px'; 76 | 77 | document.documentElement.style.setProperty('--color-primary', 'orangered'); 78 | 79 | // Attributes 80 | const logo = document.querySelector('.nav__logo'); 81 | console.log(logo.alt); 82 | console.log(logo.src); 83 | console.log(logo.className); 84 | 85 | logo.alt = 'Beautiful minimilist logo'; 86 | 87 | // Non-standard 88 | console.log(logo.designer); 89 | console.log(logo.getAttribute('designer')); 90 | logo.setAttribute('company', 'Bankist'); 91 | 92 | console.log(logo.src); // Absolute Path 93 | console.log(logo.getAttribute('src')); // Relative Path 94 | 95 | const link = document.querySelector('.twitter-link'); 96 | console.log(link.href); 97 | 98 | // Data attributes 99 | console.log(logo.dataset.versionNumber); 100 | 101 | // Classes 102 | logo.classList.add('c', 'j'); 103 | logo.classList.remove('c', 'j'); 104 | logo.classList.toggle('c'); 105 | logo.classList.contains('c'); // not includes as in arrays 106 | 107 | // Don't use 108 | logo.classList = 'xoraus'; 109 | ``` 110 | 111 | ## Implementing Smooth Scrolling 112 | ```js 113 | const buttonTo = document.querySelector('.btn--scroll-to'); 114 | const section1 = document.querySelector('#section--1'); 115 | 116 | buttonTo.addEventListener('click', function (e) { 117 | const s1cords = section1.getBoundingClientRect(); 118 | console.log(s1cords); 119 | 120 | console.log(e.target.getBoundingClientRect()); 121 | 122 | console.log('Current scroll (X/Y', window.pageXOffset, window.pageYOffset); 123 | 124 | console.log( 125 | 'height/width viewport', 126 | document.documentElement.clientHeight, 127 | document.documentElement.clientWidth 128 | ); 129 | 130 | // scrolling - Old Implemetion 131 | // window.scrollTo( 132 | // s1cords.left + window.pageXOffset, 133 | // s1cords.top + window.pageYOffset 134 | // ); 135 | 136 | // window.scrollTo({ 137 | // left: s1cords.left + window.pageXOffset, 138 | // top: s1cords.top + window.pageYOffset, 139 | // behavior: 'smooth', 140 | // }); 141 | 142 | // Modern way of implementing scrolling - works only in modern browsers 143 | 144 | section1.scrollIntoView({ behavior: 'smooth' }); 145 | }); 146 | ``` 147 | 148 | ## Types of Events and Event Handlers 149 | - There are two ways why addEventListener is better. The first one is that it allows us to add multiple event listeners to the same event. And the second one is that we can actually remove an event handler in case we don't need it anymore. 150 | ```js 151 | const h1 = document.querySelector('h1'); 152 | 153 | const alertH1 = function (e) { 154 | alert('addEventListerner: Great! You are reading the heading :D'); 155 | h1.removeEventListener('mouseenter', alertH1); 156 | }; 157 | 158 | h1.addEventListener('mouseenter', alertH1); 159 | 160 | // Removing event listener after 3 seconds 161 | setTimeout(() => h1.removeEventListener('mouseenter', alertH1), 3000); 162 | 163 | 164 | /* 165 | // However, this way of listening to events is a bit old school. 166 | h1.onmouseenter = function (e) { 167 | alert('addEventListerner: Great! You are reading the heading :D'); 168 | }; 169 | */ 170 | ``` 171 | 172 | ## Event Propagation: Bubbling and Capturing 173 | **Bubbling and Capturing** 174 | ![[12. Advanced DOM and Events-1663427457918.jpeg]] 175 | - Now by default, events can only be handled in the target, and in the bubbling phase. 176 | However, we can set up event listeners in a way that they listen to events in the capturing phase instead. Also, actually not all types of events that do have a capturing and bubbling phase. Some of them are created right on the target element, and so we can only handle them there. 177 | - We can also say that events propagate, which is really what capturing and bubbling is. It's events propagating from one place to another. 178 | 179 | ## Event Propagation in Practice 180 | - The event handler functions are listening for click events that happen on the element itself, and they are also listening for events that keep bubbling up from their child elements and that's the reason why the color changes in all of the parent elements here as well. 181 | - At event listener, it's only listening for events in the bubbling phase, but not in the capturing phase. So that is the default behavior of the add event listener method, and the reason for that is that the capturing phase is usually irrelevant for us. 182 | - Now, on the other hand, the bubbling phase can be very useful for something called event delegation. 183 | - Capturing is actually rarely used these days. And the only reason why both o capturing and bubbling actually exist, is only for historical reasons. So, from the time where different browsers implemented different versions of JavaScript. 184 | ```js 185 | const randomInt = (min, max) => 186 | Math.floor(Math.random() * (max - min + 1) + min); 187 | const randomColor = () => 188 | `rgb(${randomInt(0, 255)},${randomInt(0, 255)},${randomInt(0, 255)})`; 189 | 190 | document.querySelector('.nav__link').addEventListener('click', function (e) { 191 | this.style.backgroundColor = randomColor(); 192 | console.log('LINK', e.target, e.currentTarget); 193 | console.log(e.currentTarget === this); 194 | 195 | // Stop propagation 196 | // e.stopPropagation(); 197 | }); 198 | 199 | document.querySelector('.nav__links').addEventListener('click', function (e) { 200 | this.style.backgroundColor = randomColor(); 201 | console.log('CONTAINER', e.target, e.currentTarget); 202 | }); 203 | 204 | document.querySelector('.nav').addEventListener('click', function (e) { 205 | this.style.backgroundColor = randomColor(); 206 | console.log('NAV', e.target, e.currentTarget); 207 | }); 208 | ``` 209 | ![[12. Advanced DOM and Events-1663431880090.jpeg]] 210 | 211 | ## Event Delegation: Implementing Page Navigation 212 | ```js 213 | // Page Navigation 214 | 215 | document.querySelectorAll('.nav__link').forEach(function (el) { 216 | el.addEventListener('click', function (e) { 217 | e.preventDefault(); 218 | const id = this.getAttribute('href'); 219 | document.querySelector(id).scrollIntoView({ behavior: 'smooth' }); 220 | }); 221 | }); 222 | 223 | /* Now, as you see, this actually works just fine, but the problem is that it's not really efficient. 224 | So we are adding here the exact same callback function, so this event handler here, we are adding it once to each of these three elements. 225 | So the exact same function is now attached to these three elements. And that's kind of unnecessary. And it's really just not a clean solution in that case. And so, the better solution without a doubt, is to use events delegation. */ 226 | 227 | // 1. Add event listener to common parent element 228 | // 2. Determine what element originated the elvent 229 | 230 | document.querySelector('.nav__links').addEventListener('click', function (e) { 231 | e.preventDefault(); 232 | 233 | // Matching strategy - The tricky part 234 | if (e.target.classList.contains('nav__link')) { 235 | const id = e.target.getAttribute('href'); 236 | document.querySelector(id).scrollIntoView({ behavior: 'smooth' }); 237 | } 238 | }); 239 | ``` 240 | 241 | ## DOM Traversing 242 | ```js 243 | const h1 = document.querySelector('h1'); 244 | 245 | // Going downwards: child 246 | console.log(h1.querySelectorAll('.highlight')); 247 | console.log(h1.childNodes); 248 | console.log(h1.children); 249 | 250 | h1.firstElementChild.style.color = 'white'; 251 | h1.lastElementChild.style.color = 'orangered'; 252 | 253 | // Going upwards 254 | console.log(h1.parentNode); 255 | console.log(h1.parentElement); 256 | 257 | h1.closest('.header').style.background = 'var(--gradient-secondary)'; 258 | 259 | h1.closest('h1').style.background = 'var(--gradient-primary)'; 260 | 261 | // Going sideways: siblings 262 | console.log(h1.previousElementSibling); 263 | console.log(h1.nextElementSibling); 264 | 265 | console.log(h1.previousSibling); 266 | console.log(h1.nextSibling); 267 | 268 | console.log(h1.parentElement.children); 269 | [...h1.parentElement.children].forEach(function (el) { 270 | if (el !== h1) { 271 | el.style.transform = 'scale(0.5)'; 272 | } 273 | }); 274 | 275 | ``` 276 | 277 | ![[12. Advanced DOM and Events-1663484588539.jpeg]] 278 | 279 | ## Tabbed Component 280 | ```js 281 | /////////////////////////////////////// 282 | // Tabbed component 283 | 284 | tabsContainer.addEventListener('click', function (e) { 285 | const clicked = e.target.closest('.operations__tab'); 286 | 287 | // Guard clause 288 | if (!clicked) return; 289 | 290 | // Remove active classes 291 | tabs.forEach(t => t.classList.remove('operations__tab--active')); 292 | tabsContent.forEach(c => c.classList.remove('operations__content--active')); 293 | 294 | // Activate tab 295 | clicked.classList.add('operations__tab--active'); 296 | 297 | // Activate content area 298 | document 299 | .querySelector(`.operations__content--${clicked.dataset.tab}`) 300 | .classList.add('operations__content--active'); 301 | }); 302 | ``` 303 | 304 | ![[12. Advanced DOM and Events-1663523150947.jpeg]] 305 | 306 | ## Passing Arguments to Event Handlers 307 | ```js 308 | /////////////////////////////////////// 309 | // Menu fade animation 310 | const handleHover = function (e) { 311 | if (e.target.classList.contains('nav__link')) { 312 | const link = e.target; 313 | const siblings = link.closest('.nav').querySelectorAll('.nav__link'); 314 | const logo = link.closest('.nav').querySelector('img'); 315 | 316 | siblings.forEach(el => { 317 | if (el !== link) el.style.opacity = this; 318 | }); 319 | logo.style.opacity = this; 320 | } 321 | }; 322 | 323 | // Passing "argument" into handler 324 | nav.addEventListener('mouseover', handleHover.bind(0.5)); 325 | nav.addEventListener('mouseout', handleHover.bind(1)); 326 | ``` 327 | 328 | ![[12. Advanced DOM and Events-1663524394907.jpeg]] 329 | 330 | ## Implementing a Sticky Navigation: The Scroll Event 331 | ```js 332 | // Sticky navigation 333 | const initialCords = section1.getBoundingClientRect(); 334 | window.addEventListener('scroll', function () { 335 | if (this.window.scrollY > initialCords.top) nav.classList.add('sticky'); 336 | else nav.classList.remove('sticky'); 337 | }); 338 | ``` 339 | - `window.scrollY` this is a very inefficient method. 340 | 341 | ## A Better Way: The Intersection Observer API 342 | - Well, this API allows our code to basically observe changes to the way that a certain target element intersects another element, or the way it intersects the viewport. 343 | ```js 344 | /////////////////////////////////////// 345 | // Sticky navigation: Intersection Observer API 346 | 347 | const header = document.querySelector('.header'); 348 | const navHeight = nav.getBoundingClientRect().height; 349 | 350 | const stickyNav = function (entries) { 351 | const [entry] = entries; 352 | // console.log(entry); 353 | 354 | if (!entry.isIntersecting) nav.classList.add('sticky'); 355 | else nav.classList.remove('sticky'); 356 | }; 357 | 358 | const headerObserver = new IntersectionObserver(stickyNav, { 359 | root: null, 360 | threshold: 0, 361 | rootMargin: `-${navHeight}px`, 362 | }); 363 | 364 | headerObserver.observe(header); 365 | ``` 366 | 367 | ## Revealing Elements on Scroll 368 | ```js 369 | /////////////////////////////////////// 370 | // Reveal sections 371 | const allSections = document.querySelectorAll('.section'); 372 | 373 | const revealSection = function (entries, observer) { 374 | const [entry] = entries; 375 | 376 | if (!entry.isIntersecting) return; 377 | 378 | entry.target.classList.remove('section--hidden'); 379 | observer.unobserve(entry.target); 380 | }; 381 | 382 | const sectionObserver = new IntersectionObserver(revealSection, { 383 | root: null, 384 | threshold: 0.15, 385 | }); 386 | 387 | allSections.forEach(function (section) { 388 | sectionObserver.observe(section); 389 | section.classList.add('section--hidden'); 390 | }); 391 | ``` 392 | 393 | ## Lazy Loading Images 394 | ```js 395 | // Lazy loading images 396 | const imgTargets = document.querySelectorAll('img[data-src]'); 397 | 398 | const loadImg = function (entries, observer) { 399 | const [entry] = entries; 400 | 401 | if (!entry.isIntersecting) return; 402 | 403 | // Replace src with data-src 404 | entry.target.src = entry.target.dataset.src; 405 | 406 | entry.target.addEventListener('load', function () { 407 | entry.target.classList.remove('lazy-img'); 408 | }); 409 | 410 | observer.unobserve(entry.target); 411 | }; 412 | 413 | const imgObserver = new IntersectionObserver(loadImg, { 414 | root: null, 415 | threshold: 0, 416 | rootMargin: '200px', 417 | }); 418 | 419 | imgTargets.forEach(img => imgObserver.observe(img)); 420 | ``` 421 | 422 | ## Building a Slider Component: Part 1 & Part 2 423 | ```js 424 | /////////////////////////////////////// 425 | // Slider 426 | const slider = function () { 427 | const slides = document.querySelectorAll('.slide'); 428 | const btnLeft = document.querySelector('.slider__btn--left'); 429 | const btnRight = document.querySelector('.slider__btn--right'); 430 | const dotContainer = document.querySelector('.dots'); 431 | 432 | let curSlide = 0; 433 | const maxSlide = slides.length; 434 | 435 | // Functions 436 | const createDots = function () { 437 | slides.forEach(function (_, i) { 438 | dotContainer.insertAdjacentHTML( 439 | 'beforeend', 440 | `` 441 | ); 442 | }); 443 | }; 444 | 445 | const activateDot = function (slide) { 446 | document 447 | .querySelectorAll('.dots__dot') 448 | .forEach(dot => dot.classList.remove('dots__dot--active')); 449 | 450 | document 451 | .querySelector(`.dots__dot[data-slide="${slide}"]`) 452 | .classList.add('dots__dot--active'); 453 | }; 454 | 455 | const goToSlide = function (slide) { 456 | slides.forEach( 457 | (s, i) => (s.style.transform = `translateX(${100 * (i - slide)}%)`) 458 | ); 459 | }; 460 | 461 | // Next slide 462 | const nextSlide = function () { 463 | if (curSlide === maxSlide - 1) { 464 | curSlide = 0; 465 | } else { 466 | curSlide++; 467 | } 468 | 469 | goToSlide(curSlide); 470 | activateDot(curSlide); 471 | }; 472 | 473 | const prevSlide = function () { 474 | if (curSlide === 0) { 475 | curSlide = maxSlide - 1; 476 | } else { 477 | curSlide--; 478 | } 479 | goToSlide(curSlide); 480 | activateDot(curSlide); 481 | }; 482 | 483 | const init = function () { 484 | goToSlide(0); 485 | createDots(); 486 | 487 | activateDot(0); 488 | }; 489 | init(); 490 | 491 | // Event handlers 492 | btnRight.addEventListener('click', nextSlide); 493 | btnLeft.addEventListener('click', prevSlide); 494 | 495 | document.addEventListener('keydown', function (e) { 496 | if (e.key === 'ArrowLeft') prevSlide(); 497 | e.key === 'ArrowRight' && nextSlide(); 498 | }); 499 | 500 | dotContainer.addEventListener('click', function (e) { 501 | if (e.target.classList.contains('dots__dot')) { 502 | const { slide } = e.target.dataset; 503 | goToSlide(slide); 504 | activateDot(slide); 505 | } 506 | }); 507 | }; 508 | slider(); 509 | ``` 510 | 511 | ## Lifecycle DOM Events 512 | ```js 513 | /////////////////////////////////////// 514 | // Lifecycle DOM Events 515 | document.addEventListener('DOMContentLoaded', function (e) { 516 | console.log('HTML parsed and DOM tree built!', e); 517 | }); 518 | 519 | window.addEventListener('load', function (e) { 520 | console.log('Page fully loaded', e); 521 | }); 522 | 523 | window.addEventListener('beforeunload', function (e) { 524 | e.preventDefault(); 525 | console.log(e); 526 | e.returnValue = ''; 527 | }); 528 | ``` 529 | 530 | ## Efficient Script Loading: defer and async 531 | **DEFER AND ASYNC SCRIPT LOADING** 532 | ![[12. Advanced DOM and Events-1663584302643.jpeg]] 533 | 534 | **REGULAR VS. ASYNC VS. DEFER** 535 | ![[12. Advanced DOM and Events-1663584339910.jpeg]] 536 | - Because defer will guarantee the correct order of execution. Now, for third party scripts, where the order does not matter, for example, an analytics software like Google Analytics, or an ad script, or something like that, then in this case, you should totally use async. 537 | - So if you need to support all browsers, then you need to put your script tag at the end of the body in and not in the head. 538 | 539 | 540 | -------------------------------------------------------------------------------- /01. JavaScript Fundamentals Part 1.md: -------------------------------------------------------------------------------- 1 | # 1. JavaScript Fundamentals Part 1 2 | --- 3 | 4 | ## 1. Hello World 5 | ```js 6 | console.log("Hello World!") 7 | ``` 8 | 9 | ## 2. A brief introduction to Javascript 10 | 11 | _JavaScript_ was initially created to “make web pages alive”. 12 | 13 | The programs in this language are called _scripts_. They can be written right in a web page’s HTML and run automatically as the page loads. 14 | 15 | Scripts are provided and executed as plain text. They don’t need special preparation or compilation to run. 16 | 17 | ## 3. Linking a JavaScript File 18 | 19 | ```js 20 | let js = "amazing"; 21 | console.log(40 + 8 + 23 - 10); 22 | ``` 23 | 24 | 25 | ## 4. Values and Variables 26 | 27 | ```js 28 | console.log("xoraus"); 29 | console.log(23); 30 | 31 | let firstName = "Matilda"; 32 | 33 | console.log(firstName); 34 | console.log(firstName); 35 | console.log(firstName); 36 | ``` 37 | 38 | 5. ## Variable name conventions 39 | 40 | ```js 41 | let xoraus_matilda = "JM"; 42 | let $function = 27; 43 | 44 | let person = "xoraus"; 45 | let PI = 3.1415; 46 | 47 | let myFirstJob = "Coder"; 48 | let myCurrentJob = "Teacher"; 49 | 50 | let job1 = "programmer"; 51 | let job2 = "teacher"; 52 | 53 | console.log(myFirstJob); 54 | ``` 55 | 56 | ## 6. Data Types 57 | 58 | ```js 59 | let javascriptIsFun = true; 60 | console.log(javascriptIsFun); 61 | 62 | console.log(typeof true); 63 | console.log(typeof javascriptIsFun); 64 | console.log(typeof 23); 65 | console.log(typeof 'xoraus'); 66 | 67 | javascriptIsFun = 'YES!'; 68 | console.log(typeof javascriptIsFun); 69 | 70 | let year; 71 | 72 | console.log(year); 73 | console.log(typeof year); 74 | 75 | year = 1991; 76 | 77 | console.log(typeof year); 78 | console.log(typeof null); 79 | 80 | let, const and var // us let when you're sure that the vaule will change in future 81 | 82 | // let is block scoped 83 | // VAR is function-scoped 84 | 85 | let age = 30; 86 | age = 31; // here let (age) is mutable 87 | 88 | const birthYear = 1991; // it is immutable 89 | birthYear = 1990; 90 | 91 | const job; // this will throw an error; 92 | var job = 'programmer'; // used in legacy codebases 93 | 94 | job = 'teacher' 95 | 96 | 97 | lastName = 'Ahmed'; 98 | console.log(lastName); 99 | ``` 100 | 101 | ## 7. Basic Operators 102 | 103 | ###### Math operators 104 | 105 | ```js 106 | const now = 2037; 107 | const ageXoraus = now - 1991; 108 | const ageSarah = now - 2018; 109 | 110 | console.log(ageXoraus, ageSarah); 111 | console.log(ageXoraus * 2, ageXoraus 10, 2 ** 3); 112 | 113 | // 2 ** 3 means 2 to the power of 3 = 2 * 2 * 2 114 | 115 | const firstName = 'xoraus'; 116 | const lastName = 'Ahmed'; 117 | 118 | console.log(firstName + ' ' + lastName); 119 | ``` 120 | ###### Assignment operators 121 | 122 | ```js 123 | let x = 10 + 5; // output: 15 124 | 125 | x += 10; // x = x + 10 - Output: 25 126 | 127 | x *= 4; // x = x * 4 - Output: 100 128 | 129 | x++; // x = x + 1 130 | x--; // x = x - 1 131 | x--; 132 | 133 | console.log(x); 134 | ``` 135 | 136 | ###### Comparison operators 137 | 138 | ```js 139 | console.log(ageXoraus > ageSarah); // >, <, >=, <= 140 | console.log(ageSarah >= 18); // Output: true 141 | 142 | const isFullAge = ageSarah >= 18; 143 | 144 | console.log(now - 1991 > now - 2018); 145 | ``` 146 | 147 | ## 8. Operator Precedence 148 | 149 | ```js 150 | const now = 2037; 151 | const ageXoraus = now - 1991; 152 | const ageSarah = now - 2018; 153 | 154 | console.log(now - 1991 > now - 2018); 155 | 156 | let x, y; 157 | 158 | x = y = 25 - 10 - 5; // Output: x = y = 10, x = 10 - Assignment works from right to left 159 | 160 | console.log(x, y); 161 | 162 | const averageAge = (ageXoraus + ageSarah) 2; 163 | 164 | console.log(ageXoraus, ageSarah, averageAge); 165 | ``` 166 | 167 | ## 9. Coding Challenge ##1 168 | 169 | Mark and John are trying to compare their BMI (Body Mass Index), which is calculated using the formula: BMI = mass / height ** 2 = mass / (height * height). (mass in kg and height in meter). 170 | 171 | 1. Store Mark's and John's mass and height in variables 172 | 2. Calculate both their BMIs using the formula (you can even implement both versions) 173 | 3. Create a boolean variable 'markHigherBMI' containing information about whether Mark has a higher BMI than John. 174 | 175 | - TEST DATA 1: Marks weights 78 kg and is 1.69 m tall. John weights 92 kg and is 1.95 m tall. 176 | - TEST DATA 2: Marks weights 95 kg and is 1.88 m tall. John weights 85 kg and is 1.76 m tall. 177 | 178 | **My Solution** 179 | ```js 180 | const markMass = 78; 181 | const markHeight = 1.69; 182 | 183 | const johnMass = 92; 184 | const johnHeight = 1.95; 185 | 186 | function calculateBMI(mass, height) { 187 | let BMI; 188 | BMI = mass / height ** 2; 189 | return BMI 190 | } 191 | 192 | const markBMI = calculateBMI(markMass,markHeight); 193 | const johnBMI = calculateBMI(johnMass, johnHeight); 194 | const markHigherBMI = markBMI > johnBMI 195 | console.log(markBMI) 196 | console.log(johnBMI) 197 | console.log(markHigherBMI) 198 | ``` 199 | 200 | **Solution by xoraus** 201 | 202 | ```js 203 | const massMark = 78; 204 | const heightMark = 1.69; 205 | 206 | const massJohn = 92; 207 | const heightJohn = 1.95; 208 | 209 | const massMark = 95; 210 | const heightMark = 1.88; 211 | 212 | const massJohn = 85; 213 | const heightJohn = 1.76; 214 | 215 | const BMIMark = massMark heightMark ** 2; 216 | const BMIJohn = massJohn (heightJohn * heightJohn); 217 | 218 | const markHigherBMI = BMIMark > BMIJohn; 219 | 220 | console.log(BMIMark, BMIJohn, markHigherBMI); 221 | ``` 222 | 223 | ## 10. Strings and Template Literals 224 | 225 | ```js 226 | const firstName = 'xoraus'; 227 | const job = 'teacher'; 228 | 229 | const birthYear = 1991; 230 | const year = 2037; 231 | 232 | const xoraus = "I'm " + firstName + ', a ' + (year - birthYear) + ' year old ' + job + '!'; // concept of type coercion 233 | 234 | console.log(xoraus); 235 | 236 | const xorausNew = `I'm ${firstName}, a ${year - birthYear} year old ${job}!`; // one of most used ES6 Feature :) 237 | 238 | console.log(xorausNew); 239 | 240 | console.log(`Just a regular string...`); 241 | 242 | console.log('String with \n\ 243 | multiple \n\ 244 | lines'); 245 | 246 | console.log(`String 247 | multiple 248 | lines`); 249 | ``` 250 | 251 | ## 11. Taking Decisions: if else Statements 252 | 253 | ```js 254 | const age = 15; 255 | if (age >= 18) { 256 | console.log('Sarah can start driving license 🚗'); 257 | } else { 258 | const yearsLeft = 18 - age; 259 | console.log(`Sarah is too young. Wait another ${yearsLeft} years :)`); 260 | } 261 | 262 | const birthYear = 2012; 263 | 264 | let century; 265 | if (birthYear <= 2000) { 266 | century = 20; 267 | } else { 268 | century = 21; 269 | } 270 | console.log(century); 271 | ``` 272 | 273 | ## 12. Coding Challenge ##2 274 | 275 | Use the BMI example from Challenge ##1, and the code you already wrote, and improve it: 276 | 277 | 1. Print a nice output to the console, saying who has the higher BMI. The message can be either "Mark's BMI is higher than John's!" or "John's BMI is higher than Mark's!" 278 | 2. Use a template literal to include the BMI values in the outputs. Example: "Mark's BMI (28.3) is higher than John's (23.9)!" 279 | 280 | **My Solution** 281 | 282 | ```js 283 | const markMass = 78; 284 | const markHeight = 1.69; 285 | 286 | const johnMass = 92; 287 | const johnHeight = 1.95; 288 | 289 | function calculateBMI(mass, height) { 290 | let BMI; 291 | BMI = mass / height ** 2; 292 | return BMI 293 | } 294 | 295 | const markBMI = calculateBMI(markMass,markHeight); 296 | const johnBMI = calculateBMI(johnMass, johnHeight); 297 | 298 | // console.log(`BMI of Mark is ${markBMI}`) 299 | // console.log(`BMI of John is ${johnBMI}`) 300 | // console.log(`Does Mark has higher BMI - ${markHigherBMI}`) 301 | 302 | if (markBMI > johnBMI) { 303 | console.log(`Mark's BMI (${markBMI}) is higher than John's (${johnBMI})!`) 304 | } else { 305 | console.log(`John's BMI (${johnBMI}) is higher than Marks's (${markBMI})!`) 306 | } 307 | ``` 308 | 309 | **Another Solution** 310 | 311 | ```js 312 | const massMark = 78; 313 | const heightMark = 1.69; 314 | 315 | const massJohn = 92; 316 | const heightJohn = 1.95; 317 | 318 | 319 | const massMark = 95; 320 | const heightMark = 1.88; 321 | 322 | const massJohn = 85; 323 | const heightJohn = 1.76; 324 | 325 | const BMIMark = massMark heightMark ** 2; 326 | const BMIJohn = massJohn (heightJohn * heightJohn); 327 | 328 | console.log(BMIMark, BMIJohn); 329 | 330 | if (BMIMark > BMIJohn) { 331 | console.log(`Mark's BMI (${BMIMark}) is higher than John's (${BMIJohn})!`) 332 | } else { 333 | console.log(`John's BMI (${BMIJohn}) is higher than Marks's (${BMIMark})!`) 334 | } 335 | ``` 336 | 337 | ## 13. Type Conversion and Coercion 338 | ```js 339 | // type conversion - type conversion is when we manuallyconvert from one type to another. 340 | 341 | const inputYear = '1991'; 342 | 343 | console.log(Number(inputYear), inputYear); 344 | console.log(Number(inputYear) + 18); 345 | 346 | console.log(Number('xoraus')); // Outuput: NaN (Not a Number - it actually means invalid number) 347 | console.log(typeof NaN); // output: number 348 | 349 | console.log(String(23), 23); // String(23) is a string; 23 is a number 350 | 351 | // type coercion - type coercion is when JavaScript automatically converts types behind the scenes for us. 352 | // So basically, type coercion happens whenever an operator is dealing with two values that have different types. 353 | 354 | 355 | console.log('I am ' + 23 + ' years old'); // output: I am 23 years old. Since Javascript has type coercion, the number will be automatically be converted into string. 356 | console.log('23' - '10' - 3); // output: Here the strings are converted to numbers because of (- minus) operator. 357 | console.log('23' * '2'); // strings are converted to numbers 358 | console.log('23' / '2'); // strings are converted to numbers 359 | 360 | let n = '1' + 1; // String - '11' 361 | n = n - 1; // String 11 will be converted to number 11 then 11 - 1 = 10 362 | 363 | console.log(n); // 10 364 | 365 | console.log(2+3+4+'5') // output: 95 as string ; 9 + '5' → 95 366 | console.log('10'-'4'-'3'-2 +'5') // output: 15 as string ; 1 + '5' → 15 367 | 368 | ``` 369 | 370 | ## 14. Truthy and Falsy Values 371 | 372 | ```js 373 | // In javascript there are 5 falsy values: 0, '', undefined, null, NaN 374 | console.log(Boolean(0)); // output: false 375 | console.log(Boolean(undefined)); // output: false 376 | console.log(Boolean('Jonas')); // output: true 377 | console.log(Boolean({})); // output: true 378 | console.log(Boolean('')); // output: false 379 | 380 | const money = 100; 381 | if (money) { 382 | console.log("Don't spend it all ;)"); 383 | } else { 384 | console.log('You should get a job!'); 385 | } 386 | 387 | let height = 0; 388 | if (height) { 389 | console.log('YAY! Height is defined'); 390 | } else { 391 | console.log('Height is UNDEFINED'); // this will be the ouput because 0 is a falsy value. But this is a bug because we have defined height as 0. We can fix this using logical operators. 392 | } 393 | ``` 394 | So in practice, the conversion to boolean is always implicit, not explicit, or in other words is always typed coercion that JavaScript does automatically behind the scenes. 395 | 396 | But when exactly does JavaScript do type coercion to booleans? Well, it happens in two scenarios. First, when using logical operators, and Second in logical context, like for example, in the condition of an if else statement. 397 | 398 | ## 15. Equality Operators: == vs. === 399 | 400 | ```js 401 | const age = '18'; 402 | if (age === 18) console.log('You just became an adult :D (strict)'); // output: true (strict equality doesn't perform type coercion) 403 | if (age == 18) console.log('You just became an adult :D (loose)'); // Lose equality does the type coercion therefore output: true (the string '18' will be converted to number before checking) 404 | 405 | const favourite = Number(prompt("What's your favourite number?")); 406 | console.log(favourite); 407 | console.log(typeof favourite); 408 | 409 | if (favourite === 23) { // 22 === 23 -> FALSE 410 | console.log('Cool! 23 is an amzaing number!') 411 | } else if (favourite === 7) { 412 | console.log('7 is also a cool number') 413 | } else if (favourite === 9) { 414 | console.log('9 is also a cool number') 415 | } else { 416 | console.log('Number is not 23 or 7 or 9') 417 | } 418 | 419 | if (favourite !== 23) console.log('Why not 23?'); 420 | ``` 421 | So as a general rule for clean code, avoid the loose equality operator as much as you can. So when comparing values, always use strict equality with the three equal signs, 422 | 423 | ## 16. Logical Operators 424 | 425 | ```js 426 | const hasDriversLicense = true; // A 427 | const hasGoodVision = true; // B 428 | 429 | console.log(hasDriversLicense && hasGoodVision); // output: true 430 | console.log(hasDriversLicense || hasGoodVision); // output: true 431 | console.log(!hasDriversLicense); // output: false 432 | 433 | // if (hasDriversLicense && hasGoodVision) { 434 | // console.log('Sarah is able to drive!'); 435 | // } else { 436 | // console.log('Someone else should drive...'); 437 | // } 438 | 439 | const isTired = false; // C 440 | console.log(hasDriversLicense && hasGoodVision && isTired); output: false 441 | 442 | if (hasDriversLicense && hasGoodVision && !isTired) { 443 | console.log('Sarah is able to drive!'); 444 | } else { 445 | console.log('Someone else should drive...'); 446 | } 447 | // output: Sarah is able to drive! 448 | ``` 449 | 450 | The NOT operator actually has proceedings over the OR and AND operators. 451 | 452 | ## 17. Coding Challenge ##3 453 | 454 | There are two gymnastics teams, Dolphins and Koalas. They compete against each other 3 times. The winner with the highest average score wins the a trophy! 455 | 1. Calculate the average score for each team, using the test data below 456 | 2. Compare the team's average scores to determine the winner of the competition, and print it to the console. Don't forget that there can be a draw, so test for that as well (draw means they have the same average score). 457 | 3. BONUS 1: Include a requirement for a minimum score of 100. With this rule, a team only wins if it has a higher score than the other team, and the same time a score of at least 100 points. HINT: Use a logical operator to test for minimum score, as well as multiple else-if blocks 😉 458 | 4. BONUS 2: Minimum score also applies to a draw! So a draw only happens when both teams have the same score and both have a score greater or equal 100 points. Otherwise, no team wins the trophy. 459 | 460 | TEST DATA: Dolphins score 96, 108 and 89. Koalas score 88, 91 and 110 461 | TEST DATA BONUS 1: Dolphins score 97, 112 and 101. Koalas score 109, 95 and 123 462 | TEST DATA BONUS 2: Dolphins score 97, 112 and 101. Koalas score 109, 95 and 106 463 | 464 | **My Solution** 465 | 466 | ```js 467 | const scoreDolphins = (96 + 108 + 89) / 3; 468 | const scoreKoalas = (88 + 91 + 110) / 3; 469 | 470 | console.log(scoreDolphins, scoreKoalas); 471 | 472 | if (scoreDolphins > scoreKoalas) { 473 | console.log('Dolphins win the trophy 🏆'); 474 | } else if (scoreKoalas > scoreDolphins) { 475 | console.log('Koalas win the trophy 🏆'); 476 | } else if (scoreDolphins === scoreKoalas) { 477 | console.log('Both win the trophy!'); 478 | } 479 | ``` 480 | 481 | BONUS 1 482 | 483 | ```js 484 | const scoreDolphins = (97 + 112 + 80) 3; 485 | const scoreKoalas = (109 + 95 + 50) 3; 486 | 487 | console.log(scoreDolphins, scoreKoalas); 488 | 489 | if (scoreDolphins > scoreKoalas && scoreDolphins >= 100) { 490 | console.log('Dolphins win the trophy 🏆'); 491 | } else if (scoreKoalas > scoreDolphins && scoreKoalas >= 100) { 492 | console.log('Koalas win the trophy 🏆'); 493 | } else if (scoreDolphins === scoreKoalas && scoreDolphins >= 100 && scoreKoalas >= 100) { 494 | console.log('Both win the trophy!'); 495 | } else { 496 | console.log('No one wins the trophy 😭'); 497 | } 498 | ``` 499 | 500 | ## 18. The switch Statement 501 | ```js 502 | const day = 'friday'; 503 | 504 | switch (day) { 505 | case 'monday': // day === 'monday' 506 | console.log('Plan course structure'); 507 | console.log('Go to coding meetup'); 508 | break; 509 | case 'tuesday': 510 | console.log('Prepare theory videos'); 511 | break; 512 | case 'wednesday': 513 | case 'thursday': 514 | console.log('Write code examples'); 515 | break; 516 | case 'friday': 517 | console.log('Record videos'); 518 | break; 519 | case 'saturday': 520 | case 'sunday': 521 | console.log('Enjoy the weekend :D'); 522 | break; 523 | default: 524 | console.log('Not a valid day!'); 525 | } 526 | 527 | if (day === 'monday') { 528 | console.log('Plan course structure'); 529 | console.log('Go to coding meetup'); 530 | } else if (day === 'tuesday') { 531 | console.log('Prepare theory videos'); 532 | } else if (day === 'wednesday' || day === 'thursday') { 533 | console.log('Write code examples'); 534 | } else if (day === 'friday') { 535 | console.log('Record videos'); 536 | } else if (day === 'saturday' || day === 'sunday') { 537 | console.log('Enjoy the weekend :D'); 538 | } else { 539 | console.log('Not a valid day!'); 540 | } 541 | ``` 542 | 543 | ## 19. Statements and Expressions 544 | ```js 545 | 3 + 4 // output: 7 - an expression 546 | 1991 547 | true && false && !false 548 | 549 | // Following is a Statement 550 | if (50 > 10) { 551 | const str = '50 is bigger'; 552 | } 553 | 554 | const me = 'Xoraus'; 555 | console.log(`I'm ${2050 - 1997} years old ${me}`); // In javascript literals we can use expressions but not statements. 556 | ``` 557 | 558 | - an expression is a piece of code that produces a value. 559 | - statements are like full sentences that translate our actions. 560 | 561 | ## 20. The Conditional (Ternary) Operator 562 | ```js 563 | const age = 23; 564 | // age >= 18 ? console.log('I like to drink wine 🍷') : console.log('I like to drink water 💧'); 565 | 566 | const drink = age >= 18 ? 'Sharbat-e-Jaam 🍷' : 'water 💧'; 567 | console.log(drink); 568 | 569 | let drink2; 570 | if (age >= 18) { 571 | drink2 = 'Sharbat-e-Jaam 🍷'; 572 | } else { 573 | drink2 = 'water 💧'; 574 | } 575 | console.log(drink2); 576 | 577 | console.log(`I like to drink ${age >= 18 ? 'Sharbat-e-Jaam 🍷' : 'water 💧'}`); 578 | ``` 579 | 580 | ## 21. Coding Challenge ##4 581 | 582 | Steven wants to build a very simple tip calculator for whenever he goes eating in a restaurant. In his country, it's usual to tip 15% if the bill value is between 50 and 300. If the value is different, the tip is 20%. 583 | 1. Your task is to calculate the tip, depending on the bill value. Create a variable called 'tip' for this. It's not allowed to use an if else statement 😅 (If it's easier for you, you can start with an if else statement, and then try to convert it to a ternary operator!) 584 | 2. Print a string to the console containing the bill value, the tip, and the final value (bill + tip). Example: 'The bill was 275, the tip was 41.25, and the total value 316.25' 585 | 586 | TEST DATA: Test for bill values 275, 40 and 430 587 | 588 | HINT: To calculate 20% of a value, simply multiply it by 20/100 = 0.2 589 | HINT: Value X is between 50 and 300, if it's >= 50 && <= 300 😉 590 | 591 | ```js 592 | const bill = 275; 593 | const tip = (bill >= 50 && bill <= 300) ? 0.15 * bill : 0.20 * bill; 594 | 595 | const totalValue = bill + tip; 596 | 597 | console.log(`The bill was ${bill}, the tip was ${tip}, and the total value ${bill + tip}`); 598 | ``` 599 | 600 | -------------------------------------------------------------------------------- /07. How JavaScript Works Behind the Scenes.md: -------------------------------------------------------------------------------- 1 | # 7. How JavaScript Works Behind the Scenes 2 | 3 | ## An High-Level Overview of JavaScript 4 | 5 | ### What is Javascript? 6 | Javascript Is a High-Level, Object-Oriented, Multi-Paradigm Programming Language. 7 | 8 | Javascript Is a **High-Level** _Prototype-Based_ **Object-Oriented** Multi-Paradigm Interpreted or Just-in-Time Compiled Dynamic| Single-Threaded Garbage-Collected Programming Language With First-Class Functions and a Non-Blocking Event Loop Concurrency Model 9 | 10 | #### High-level 11 | ![[7. How JavaScript Works Behind the Scenes-1661664454597.jpeg]] 12 | #### Garbage-collected 13 | ![[7. How JavaScript Works Behind the Scenes-1661664479453.jpeg]] 14 | #### Interpreted or just-in-time compiled 15 | ![[7. How JavaScript Works Behind the Scenes-1661664501052.jpeg]] 16 | #### Multi-paradigm 17 | ![[7. How JavaScript Works Behind the Scenes-1661664573105.jpeg]] 18 | #### Prototype-based object-oriented 19 | ![[7. How JavaScript Works Behind the Scenes-1661664597254.jpeg]] 20 | #### First-class functions 21 | ![[7. How JavaScript Works Behind the Scenes-1661664636245.jpeg]] 22 | 23 | #### Dynamic 24 | ![[7. How JavaScript Works Behind the Scenes-1661664652377.jpeg]] 25 | 26 | #### Single-threaded & Non-blocking event loop 27 | ![[7. How JavaScript Works Behind the Scenes-1661664673207.jpeg]] 28 | 29 | ## The JavaScript Engine and Runtime 30 | 31 | ### What is Javascript Engine 32 | ![[7. How JavaScript Works Behind the Scenes-1661670799106.jpeg]] 33 | - So a JavaScript engine is simply a computer program that executes JavaScript code. 34 | - Now every browser has its own JavaScript engine but probably the most well known engine is Google's V-Eight. 35 | - So any JavaScript engine always contains a call stack and a heap. The call stack is where our code is actually executed using something called execution contexts. Then the heap is an unstructured memory pool which stores all the objects that our application needs. 36 | ### Compilation vs Interpretation 37 | 38 | ![[7. How JavaScript Works Behind the Scenes-1661670833417.jpeg]] 39 | 40 | 41 | ### Just in Time Compilation of Javascript 42 | ![[7. How JavaScript Works Behind the Scenes-1661670875764.jpeg]] 43 | 44 | ### Runtime in Browser 45 | ![[7. How JavaScript Works Behind the Scenes-1661670745908.jpeg]] 46 | 47 | ### Runtime in NodeJs 48 | 49 | ![[7. How JavaScript Works Behind the Scenes-1661670767221.jpeg]] 50 | 51 | ## Execution Contexts and The Call Stack 52 | ### What is an Execution Context? 53 | ![[7. How JavaScript Works Behind the Scenes-1661673934102.jpeg]] 54 | 55 | But now what exactly is an execution context? 56 | Well, an execution context is an abstract concept. 57 | But I define it basically as an environment in which a piece of JavaScript is executed. a It's like a box that stores all the necessary information for some code to be executed. 58 | Such as local variables or arguments passed into a function. 59 | So, JavaScript code always runs inside an execution context. 60 | 61 | Now, in any JavaScript project, no matter how large it is, there is only ever one global execution context. It's always there as the default context, and it's where top-level code will execute. 62 | 63 | ### Execution Context in Detail 64 | ![[7. How JavaScript Works Behind the Scenes-1661673969086.jpeg]] 65 | 66 | Because remember each function gets its own execution context 67 | as soon as the function is called. So basically all the variables that are somehow declared inside a function, will end up in its variable environment. However, a function can also access variables outside of the function and this works because of something called the scope chain. 68 | 69 | Scope chain basically consists of references to variables that are located outside of the current function. And to keep track of the scope chain, it is stored in each execution context. 70 | 71 | Execution contexts belonging to arrow functions, do not get their own arguments keyword, nor do they get the this keyword, okay? So, basically arrow functions don't have the arguments object and the this keyword. Instead, they can use the arguments object, and the this keyword from their closest regular function parent. 72 | 73 | ### The Call Stack 74 | ![[7. How JavaScript Works Behind the Scenes-1661674004333.jpeg]] 75 | 76 | JavaScript has only one thread of execution. And so it can only do one thing at a time. 77 | 78 | So like to use the analogy of the call stack being like a map for the JavaScript engine. 79 | Because the call stack ensures that the order of execution never gets lost. 80 | ## Scope and The Scope Chain 81 | ### Scope Concepts 82 | - **Scoping**: How our program's variables are organized and accessed. "Where do variables live?" or "Where can we access a certain variable, and where not?", 83 | - **Lexical scoping**: Scoping is controlled by placement of functions and blocks in the code; 84 | - **Scope**: Space or environment in which a certain variable is declared (variable environment in case of functions). There is global scope, function scope, and block scope; 85 | - **Scope of a variable**: Region of our code where a certain variable can be accessed. 86 | 87 | ### The 3 types of Scopes 88 | ![[7. How JavaScript Works Behind the Scenes-1661745363013.jpeg]] 89 | 90 | ### Scope Chain vs. The Call Stack 91 | ![[7. How JavaScript Works Behind the Scenes-1661745401792.jpeg]] 92 | 93 | ### Summary 94 | - Scoping asks the question "Where do variables live?" or "Where can we access a certain variable, and where not?"; 95 | - There are 3 types of scope in JavaScript: the global scope, scopes defined by functions, and scopes defined by blocks; 96 | - Only let and const variables are block-scoped. Variables declared with var end up in the closest function scope; 97 | - In JavaScript, we have lexical scoping, so the rules of where we can access variables are based on exactly where in the code functions and blocks are written; 98 | - Every scope always has access to all the variables from all its outer scopes. This is the scope chain! 99 | - When a variable is not in the current scope, the engine looks up in the scope chain until it finds the variable it's looking for. This is called variable lookup; 100 | - The scope chain is a one-way street: a scope will never, ever have access to the variables of an inner scope; 101 | - The scope chain in a certain scope is equal to adding together all the variable environments of the all parent scopes; 102 | - The scope chain has nothing to do with the order in which functions were called. It does not affect the scope chain at all! 103 | 104 | ## Scoping in Practice 105 | ```js 106 | function calcAge(birthYear) { 107 | const age = 2037 - birthYear; 108 | 109 | function printAge() { 110 | let output = `${firstName}, you are ${age}, born in ${birthYear}`; 111 | console.log(output); // output: firstName = Jonas → coming from global scope. 112 | 113 | // Block Scope 114 | if (birthYear >= 1981 && birthYear <= 1996) { 115 | var millenial = true; 116 | // Creating NEW variable with same name as outer scope's variable 117 | const firstName = 'Steven'; // firstName already declared in Global Scope 118 | 119 | // Reasssigning outer scope's variable 120 | output = 'NEW OUTPUT!'; 121 | 122 | const str = `Oh, and you're a millenial, ${firstName}`; // output: Steven - This happens because Javascript tries to look the variable name in the current scope. 123 | console.log(str); // output: Oh, and you're a millenial, xoraus 124 | 125 | function add(a, b) { 126 | return a + b; // The scope of this add function is only where it is defined 127 | } 128 | } 129 | console.log(str); // output: // str is not defined 130 | console.log(millenial); //output: true 131 | console.log(add(2, 3)); // output: add is not defined. (in strict mode) 132 | console.log(output); //output: NEW OUTPUT 133 | } 134 | printAge(); 135 | 136 | return age; 137 | } 138 | 139 | const firstName = 'xoraus'; 140 | calcAge(1997); // output: xoraus you are 50, born in 1997 141 | console.log(age); // output: age is not defined 142 | printAge(); // output: printAge is not defined 143 | ``` 144 | 145 | - const and let variables are block scoped. 146 | - so variables declared with the var keyword are function scoped. So they simply ignore the block, because they are not block scoped at all. They're just function scoped. 147 | - that the scope of a variable is the entire region of the code in which the variable is accessible. 148 | - Functions are block scoped in **Strict Mode** 149 | - So the scope chain isn't necessary at all, if the variable that we're looking for is already in the current scope. 150 | 151 | ## Variable Environment: Hoisting and The TDZ 152 | ![[7. How JavaScript Works Behind the Scenes-1661750446465.jpeg]] 153 | - So we learned that an execution context always contains three parts. A variable environment, the scope chain in the in current context, and the this keyword. 154 | - So in JavaScript we have a mechanism called hoisting. And hoisting basically make some types of variables accessible, or let's say usable in the code before they are actually declared in the code. Now, many people simply define hoisting by saying that variables are magically lifted off moved to the top of their scope for example, to the top of a function. And that is actually what hoisting looks like on the surface. 155 | - Instead, behind the scenes the code is basically scanned for variable declarations before it is executed. So this happens during the so-called creation phase of the execution context that we talked about before. Then for each variable that is found is in the code, a new property is created in a variable environment object. And that's how hoisting really works. Now, hoisting does not work the same for all variable types. 156 | - This means that a function expression or arrow function created with var is hoisted to _undefined_. But if created with **let** or **const**, it's not usable before it's declared in a code because of the Temporal Dead Zone so again, just like normal variables. And is this is actually the reason that we cannot use function expressions before we write them in the code, unlike function declarations. 157 | 158 | ![[7. How JavaScript Works Behind the Scenes-1661751743808.jpeg]] 159 | 160 | - So to recap, basically each and every let and const variable get their own Temporal Dead Zone that starts at the beginning of the scope until the line where it is defined. 161 | And the variable is only safe to use after the TDZ, so the Temporal Dead Zone. 162 | - Alright, now what is actually the need for JavaScript to have a Temporal Dead Zone? Well, the main reason that the TDZ was introduced in ES6 is that the behavior I described before makes it way easier to avoid and catch errors. Because using a variable that is set to undefined before it's actually declared can cause serious bugs which might be hard to find. 163 | - So accessing variables before declaration is bad practice and should be avoided. And the best way to avoid it iS by simply getting an error. 164 | - A second and smaller reason why the TDZ exists is to make const variables actually work the way they are supposed to. So as you know, we can't reassign const variables. it So it will not be possible to set them to undefined first and then assign their real value later. 165 | - Now, if hoisting creates so many problems, why does it exist in the first place? I get this question all the time. And so let's quickly talk about that here. So the creator of JavaScript basically implemented hoisting so that we can use function declarations before we use them. Because this is essential for some programming techniques, such as mutual recursion. Some people also think that it makes code a lot more readable. Now, the fact that it also works for var declarations is because that was the only way hoisting could be implemented at the time. So the hoisting of var variables is basically just a byproduct of hoisting functions. 166 | ### Hoisting Example 167 | ```js 168 | Take a look at this code 169 | 170 | test(); 171 | 172 | function test() { 173 | console.log("Hello"); 174 | } 175 | 176 | // We can call the test() function before it was declared in code. That's the hoisting in practice. 177 | 178 | // Why it's possible? 179 | 180 | // JavaScript engine scans the code before executing it and creates a property for each variable or function in the code. For normal variables, it assigns an undefined value, and for functions it assigns a reference to that function in memory. That's why we can call a function, but if we try to access a variable, we will get undefined. 181 | 182 | function scope() { 183 | console.log(var1); // undefined 184 | console.log(va1); // undefined 185 | 186 | var var1 = "Hello"; 187 | var var2 = "Hi"; 188 | } 189 | Let me know if you have any questions 190 | ``` 191 | ## Hoisting and TDZ in Practice 192 | ```js 193 | // # Hoisting with Variables 194 | console.log(me); // output: Undefined (because of Var) 195 | // console.log(job); // output: Cannot access 'job' before initialization, the origin of this error is that the jobn variable is still in temporal dead zone 196 | // console.log(year); // output: Cannot access 'year' before initialization 197 | 198 | var me = 'Jonas'; 199 | let job = 'teacher'; 200 | const year = 1991; 201 | 202 | // Functions 203 | console.log(addDecl(2, 3)); //output: 5 204 | // console.log(addExpr(2, 3)); // output: Cannot access 'addExpr' before initialization 205 | console.log(addArrow); // output: Undefined 206 | // console.log(addArrow(2, 3)); 207 | 208 | function addDecl(a, b) { 209 | return a + b; 210 | } 211 | 212 | const addExpr = function (a, b) { 213 | return a + b; 214 | }; 215 | 216 | var addArrow = (a, b) => a + b; 217 | 218 | // Example 219 | console.log(undefined); 220 | if (!numProducts) deleteShoppingCart(); // output: All products deleted because at this moment the numProducts is 'Undefined' because it is declared with 'var' and that's because how the var works with hosting 221 | 222 | var numProducts = 10; 223 | 224 | function deleteShoppingCart() { 225 | console.log('All products deleted!'); 226 | } 227 | 228 | var x = 1; // we get a property of x = 1. we cannot find y or z here in this object and that's because they were declared with let or const 229 | let y = 2; 230 | const z = 3; 231 | 232 | console.log(x === window.x); // output: true 233 | console.log(y === window.y); // output: false 234 | console.log(z === window.z); // output: false 235 | ``` 236 | 237 | - variables declared with var, will create a property on the global window object. 238 | And that can have some implications in some cases. 239 | 240 | ## The this Keyword 241 | ![[7. How JavaScript Works Behind the Scenes-1661754569885.jpeg]] 242 | 243 | - Instead, if you use 'the this variable' in an arrow function, it will simply be the this keyword of the surrounding function. So of the parent function and in technical terms, this is called the 'lexical this keyword,' because it simply gets picked up from the outer lexical scope of the arrow function. 244 | - It's also important to know what the, this keyword is not. So this will never point to the function in which we are using it. Also, the this keyword will never point to the variable environment of the function. And these are two pretty common misconceptions 245 | 246 | ## The this Keyword in Practice 247 | ```js 248 | 'use strict' 249 | // The this Keyword in Practice 250 | console.log(this); // output: Window Object 251 | 252 | const calcAge = function (birthYear) { 253 | console.log(2037 - birthYear); 254 | console.log(this); // output: Undefined (in case of strict mode) 255 | }; 256 | calcAge(1991); 257 | 258 | const calcAgeArrow = birthYear => { 259 | console.log(2037 - birthYear); 260 | console.log(this); // output: Window (because the arrow function doesn't get the this function So instead the arrow function simply uses the lexical this keyword, which means that it uses the disc keyword of its parent function or of its parents scope. 261 | }; 262 | calcAgeArrow(1980); 263 | 264 | const jonas = { 265 | year: 1991, 266 | calcAge: function () { 267 | console.log(this); // output: jonas object - The reason that the this keyword will point to Jonas in this case is because jonas was the object calling that method 268 | console.log(2037 - this.year); // output: 46 269 | }, 270 | }; 271 | jonas.calcAge(); 272 | 273 | const matilda = { 274 | year: 2017, 275 | }; 276 | 277 | matilda.calcAge = jonas.calcAge; // method borrowing 278 | matilda.calcAge(); // output: 20 (this will point to year: 2017) - So even though the method is written here inside of the Jonas object the this keyword will still point to Matilda. If it is Matilda, who calls the method. 279 | 280 | const f = jonas.calcAge; 281 | f(); // Cannot read property 'year' of undefined 282 | ``` 283 | ## Regular Functions vs. Arrow Functions 284 | ```js 285 | // Regular Functions vs. Arrow Functions 286 | // var firstName = 'Matilda'; 287 | 288 | const jonas = { 289 | firstName: 'Jonas', 290 | year: 1991, 291 | calcAge: function () { 292 | // console.log(this); 293 | console.log(2037 - this.year); 294 | 295 | // Solution 1 296 | // const self = this; // self or that 297 | // const isMillenial = function () { 298 | // console.log(self); 299 | // console.log(self.year >= 1981 && self.year <= 1996); 300 | // }; 301 | 302 | // Solution 2 303 | const isMillenial = () => { 304 | console.log(this); 305 | console.log(this.year >= 1981 && this.year <= 1996); 306 | }; 307 | isMillenial(); 308 | }, 309 | 310 | greet: () => { 311 | console.log(this); //output: Window 312 | console.log(`Hey ${this.firstName}`); //output: Matilda 313 | }, 314 | }; 315 | jonas.greet(); // output: Hey Undefined - (using the this keyword from it's parent's this keyword) 316 | jonas.calcAge(); 317 | 318 | // arguments keyword 319 | const addExpr = function (a, b) { 320 | console.log(arguments); 321 | return a + b; 322 | }; 323 | addExpr(2, 5); 324 | addExpr(2, 5, 8, 12); 325 | 326 | var addArrow = (a, b) => { 327 | console.log(arguments); 328 | return a + b; 329 | }; 330 | addArrow(2, 5, 8); 331 | 332 | ``` 333 | - Now, just like the this keyword, the arguments keyword is only available in regular functions. 334 | ## Primitives vs. Objects (Primitive vs. Reference Types) 335 | ![[7. How JavaScript Works Behind the Scenes-1661760447942.jpeg]] 336 | ```js 337 | let age = 30; 338 | let oldAge = age; 339 | age = 31; 340 | console.log(age); 341 | console.log(oldAge); 342 | 343 | const me = { 344 | name: 'Jonas', 345 | age: 30, 346 | }; 347 | const friend = me; 348 | friend.age = 27; 349 | console.log('Friend:', friend); 350 | console.log('Me', me); 351 | ``` 352 | ![[7. How JavaScript Works Behind the Scenes-1661765088951.jpeg]] 353 | 354 | ## Primitives vs. Objects in Practice 355 | ```js 356 | // Primitive types 357 | let lastName = 'Williams'; 358 | let oldLastName = lastName; 359 | lastName = 'Davis'; 360 | console.log(lastName, oldLastName); // output: Davis Williams 361 | 362 | // Reference types 363 | const jessica = { 364 | firstName: 'Jessica', 365 | lastName: 'Williams', 366 | age: 27, 367 | }; 368 | const marriedJessica = jessica; 369 | marriedJessica.lastName = 'Davis'; 370 | console.log('Before marriage:', jessica); // output: Before marriage: { firstName: 'Jessica', lastName: 'Davis', age: 27 } 371 | console.log('After marriage: ', marriedJessica); // output: After marriage: { firstName: 'Jessica', lastName: 'Davis', age: 27 } 372 | 373 | // Copying objects 374 | const jessica2 = { 375 | firstName: 'Jessica', 376 | lastName: 'Williams', 377 | age: 27, 378 | family: ['Alice', 'Bob'], 379 | }; 380 | 381 | const jessicaCopy = Object.assign({}, jessica2); 382 | jessicaCopy.lastName = 'Davis'; 383 | 384 | jessicaCopy.family.push('Mary'); 385 | jessicaCopy.family.push('John'); 386 | 387 | console.log('Before marriage:', jessica2); // family: [ 'Alice', 'Bob', 'Mary', 'John' ] 388 | console.log('After marriage: ', jessicaCopy); // amily: [ 'Alice', 'Bob', 'Mary', 'John' ] 389 | ``` 390 | - And that's why we say that this object.assign only creates a shallow copy 391 | - However, the family object is a deeply nested object. And so therefore, object.assign did not really, behind the scenes, copy it to the new object. 392 | - Now, a deep clone is what we would need here. Usually, we do something like this using an external library, for example, like LoDash, and this library has a ton of helpful tools and one is of them is for deep cloning. 393 | 394 | #### Later in course 395 | - Prototypal Inheritance Object Oriented Programming (00P) With JavaScript 396 | - Event Loop Asynchronous JavaScript: Promises, Async/Await and AJAX 397 | - How the DOM Really Works Advanced DOM and Events -------------------------------------------------------------------------------- /02. JavaScript Fundamentals Part 2.md: -------------------------------------------------------------------------------- 1 | # 2. JavaScript Fundamentals Part 2 2 | --- 3 | ## 32. Activating Strict Mode 4 | ```js 5 | 'use strict'; // to activate strict mode to 6 | ``` 7 | - Always put strict mode in the beginning of the file 8 | - It makes it easier for developer to avoid accidental errors. 9 | 10 | First, strict mode forbids us to do certain things and second, it will actually create visible errors for us in certain situations in which without strict mode JavaScript will simply fail silently without letting us know that we did a mistake. 11 | ```js 12 | 'use strict'; 13 | 14 | let hasDriversLicense = false; 15 | const passTest = true; 16 | 17 | if (passTest) hasDriversLicense = true; // if in hasDriver(s)License s is missing the code will behave unexpectedly 18 | if (hasDriversLicense) console.log('I can drive :D'); 19 | 20 | // const interface = 'Audio'; // Uncaught SyntaxError: Missing initializer in const declaration 21 | // const private = 534; // Uncaught SyntaxError: Missing initializer in const declaration 22 | ``` 23 | 24 | ## 33. Functions 25 | ```js 26 | function logger() { 27 | console.log('My name is xoraus'); 28 | } 29 | 30 | // calling / running / invoking function 31 | logger(); // output: My name is xoraus 32 | logger(); // output: My name is xoraus 33 | logger(); // output: My name is xoraus 34 | 35 | function fruitProcessor(apples, oranges) { 36 | const juice = `Juice with ${apples} apples and ${oranges} oranges.`; 37 | return juice; 38 | } 39 | 40 | const appleJuice = fruitProcessor(10, 5); 41 | console.log(appleJuice);1 42 | 43 | const appleOrangeJuice = fruitProcessor(4, 8); 44 | console.log(appleOrangeJuice); 45 | 46 | const num = Number('6833'); 47 | ``` 48 | 49 | Clean Code - **DRY Principle** that is _Don't Repeat Yourself_ 50 | 51 | ## 34. Function Declarations vs. Expressions 52 | 53 | ```js 54 | // Function declaration 55 | function calcAge1(birthYeah) { 56 | return 2047 - birthYeah; 57 | } 58 | const age1 = calcAge1(1997); 59 | // Function expression 60 | const calcAge2 = function (birthYeah) { // Anonymous Functions - this function is an expression. Expressions produce value. 61 | return 2047 - birthYeah; 62 | } 63 | const age2 = calcAge2(1997); 64 | 65 | console.log(age1, age2); 66 | ``` 67 | - The parameter is a kind of placeholder in the function and the argument is then the actual value that we use to fill in that placeholder that is the parameter. 68 | - In Javascript functions are just values 69 | 70 | ## 35. Arrow Functions 71 | ```js 72 | const calcAge3 = birthYeah => 2047 - birthYeah; 73 | const age3 = calcAge3(1997); 74 | console.log(age3); 75 | 76 | const yearsUntilRetirement = (birthYeah, firstName) => { 77 | const age = 2047 - birthYeah; 78 | const retirement = 65 - age; 79 | // return retirement; 80 | return `${firstName} retires in ${retirement} years`; 81 | } 82 | 83 | console.log(yearsUntilRetirement(1997, 'xoraus')); console.log(yearsUntilRetirement(2005, 'ahmed')); 84 | ``` 85 | 86 | ## 36. Functions Calling Other Functions 87 | ```js 88 | function cutFruitPieces(fruit) { 89 | return fruit * 4; 90 | } 91 | 92 | function fruitProcessor(apples, oranges) { 93 | const applePieces = cutFruitPieces(apples); 94 | const orangePieces = cutFruitPieces(oranges); 95 | 96 | const juice = `Juice with ${applePieces} piece of apple and ${orangePieces} pieces of orange.`; 97 | return juice; 98 | } 99 | console.log(fruitProcessor(2, 3)); 100 | ``` 101 | 102 | - Arrow functions do not get the **this Keyword** 103 | 104 | ## 37. Reviewing Functions 105 | ```js 106 | const calcAge = function (birthYeah) { 107 | return 2047 - birthYeah; 108 | } 109 | 110 | const yearsUntilRetirement = function (birthYeah, firstName) { 111 | const age = calcAge(birthYeah); 112 | const retirement = 65 - age; 113 | 114 | if (retirement > 0) { 115 | console.log(`${firstName} retires in ${retirement} years`); 116 | return retirement; 117 | } else { 118 | console.log(`${firstName} has already retired 🎉`); 119 | return -1; 120 | } 121 | } 122 | 123 | console.log(yearsUntilRetirement(1997, 'xoraus')); 124 | console.log(yearsUntilRetirement(1980, 'Jordan')); 125 | ``` 126 | ![[2. JavaScript Fundamentals Part 2-20220824162950.png]] 127 | 128 | ## 38. Coding Challenge 1 129 | Back to the two gymnastics teams, the Dolphins and the Koalas! There is a new gymnastics discipline, which works differently. Each team competes 3 times, and then the average of the 3 scores is calculated (so one average score per team). A team ONLY wins if it has at least DOUBLE the average score of the other team. Otherwise, no team wins! 130 | 131 | 1. Create an arrow function 'calcAverage' to calculate the average of 3 scores 132 | 2. Use the function to calculate the average for both teams 133 | 3. Create a function 'checkWinner' that takes the average score of each team as parameters ('avgDolhins' and 'avgKoalas'), and then logs the winner to the console, together with the victory points, according to the rule above. Example: "Koalas win (30 vs. 13)". 134 | 4. Use the 'checkWinner' function to determine the winner for both DATA 1 and DATA 2. 135 | 5. Ignore draws this time. 136 | 137 | TEST DATA 1: Dolphins score 44, 23 and 71. Koalas score 65, 54 and 49 138 | TEST DATA 2: Dolphins score 85, 54 and 41. Koalas score 23, 34 and 27 139 | 140 | HINT: To calculate average of 3 values, add them all together and divide by 3 141 | HINT: To check if number A is at least double number B, check for A >= 2 * B. Apply this to the team's average scores 😉 142 | 143 | ```js 144 | calcAverage = (scoreA, scoreB, scoreC) => (scoreA, scoreB, scoreC) / 3 145 | 146 | const teamDolphinAvg = Math.floor(calcAverage(85,54,41)); 147 | const teamKoalaAvg = Math.floor(calcAverage(23,34,27)); 148 | 149 | console.log(teamDolphinAvg,teamKoalaAvg) 150 | 151 | function checkWinner(teamDolphinAvg,teamKoalaAvg) { 152 | if (teamDolphinAvg >= 2 * teamKoalaAvg) { 153 | console.log(`Dolphins win 🏆 ${teamDolphinAvg} vs ${teamKoalaAvg}`) 154 | } else if (teamKoalaAvg >= 2 * teamDolphinAvg) { 155 | console.log(`Koalas win 🏆 ${teamKoalaAvg} vs. ${teamDolphinAvg}`) 156 | } else { 157 | console.log(`No body won :(`) 158 | } 159 | } 160 | 161 | checkWinner(800,351) 162 | 163 | // Test 2 164 | scoreDolphins = calcAverage(85, 54, 41); 165 | scoreKoalas = calcAverage(23, 34, 27); 166 | console.log(scoreDolphins, scoreKoalas); 167 | checkWinner(scoreDolphins, scoreKoalas); 168 | 169 | ``` 170 | 171 | 172 | ## 39. Introduction to Arrays 173 | ```js 174 | const friend1 = 'Al-Farabi'; 175 | const friend2 = 'Al-Ghazali'; 176 | const friend3 = 'Al- Razi'; 177 | 178 | const friends = ['Al-Farabi', 'Al-Ghazali', 'Al- Razi']; 179 | console.log(friends); 180 | 181 | const y = new Array(1991, 1984, 2008, 2020); 182 | 183 | console.log(friends[0]); // output: Al-Farabi 184 | console.log(friends[2]); // output: Al- Razi 185 | 186 | console.log(friends.length); // outpt: 3 187 | console.log(friends[friends.length - 1]); // output: Peter 188 | 189 | friends[3] = 'Al-Qalbi'; 190 | console.log(friends); // output: Al-Farabi, Al-Ghazali, Al- Razi 191 | // friends = ['Bob', 'Alice'] 192 | 193 | const firstName = 'Jordan'; 194 | const xoraus = [firstName, 'Ahmed', 2047 - 1997, 'teacher', friends]; 195 | console.log(xoraus); 196 | console.log(xoraus.length); 197 | 198 | // Exercise 199 | const calcAge = function (birthYeah) { 200 | return 2047 - birthYeah; 201 | } 202 | const years = [1990, 1967, 2002, 2010, 2018]; 203 | 204 | const age1 = calcAge(years[0]); 205 | const age2 = calcAge(years[1]); 206 | const age3 = calcAge(years[years.length - 1]); 207 | console.log(age1, age2, age3); 208 | 209 | const ages = [calcAge(years[0]), calcAge(years[1]), calcAge(years[years.length - 1])]; 210 | console.log(ages); 211 | ``` 212 | 213 | ## 40. Basic Array Operations (Methods) 214 | ```js 215 | const friends = ['Al-Farabi', 'Al-Ghazali', 'Al- Razi']; 216 | // const friends = ['Michael', 'Steven', 'Peter']; 217 | 218 | // Add elements 219 | const newLength = friends.push('xoraus'); 220 | console.log(friends); 221 | console.log(newLength); 222 | 223 | friends.unshift('Jordan'); 224 | console.log(friends); 225 | 226 | // Remove elements 227 | friends.pop(); // Last 228 | const popped = friends.pop(); 229 | console.log(popped); 230 | console.log(friends); 231 | 232 | friends.shift(); // First 233 | console.log(friends); 234 | 235 | console.log(friends.indexOf('Al-Farabi')); 236 | console.log(friends.indexOf('Al-Ghazali')); 237 | 238 | friends.push(25); 239 | console.log(friends.includes('Al- Razi')); 240 | console.log(friends.includes('Ahmed')); 241 | console.log(friends.includes(25)); 242 | 243 | if (friends.includes('Al-Ghazali')) { 244 | console.log('You have a friend called Al-Ghazali'); 245 | } 246 | ``` 247 | 248 | ## 41. Coding Challenge ##2 249 | Ahmed is still building his tip calculator, using the same rules as before: Tip 15% of the bill if the bill value is between 50 and 300, and if the value is different, the tip is 20%. 250 | 251 | 1. Write a function 'calcTip' that takes any bill value as an input and returns the corresponding tip, calculated based on the rules above (you can check out the code from first tip calculator challenge if you need to). Use the function type you like the most. Test the function using a bill value of 100. 252 | 2. And now let's use arrays! So create an array 'bills' containing the test data below. 253 | 3. Create an array 'tips' containing the tip value for each bill, calculated from the function you created before. 254 | 4. BONUS: Create an array 'total' containing the total values, so the bill + tip. 255 | 256 | TEST DATA: 125, 555 and 44 257 | 258 | HINT: Remember that an array needs a value in each position, and that value can actually be the returned value of a function! So you can just call a function as array values (so don't store the tip values in separate variables first, but right in the new array) 259 | 260 | ```js 261 | const calcTip = function (bill) { 262 | return bill >= 50 && bill <= 300 ? bill * 0.15 : bill * 0.2; 263 | } 264 | // const calcTip = bill => bill >= 50 && bill <= 300 ? bill * 0.15 : bill * 0.2; 265 | 266 | const bills = [125, 555, 44]; 267 | const tips = [calcTip(bills[0]), calcTip(bills[1]), calcTip(bills[2])]; 268 | const totals = [bills[0] + tips[0], bills[1] + tips[1], bills[2] + tips[2]]; 269 | 270 | console.log(bills, tips, totals); 271 | ``` 272 | 273 | ## 42. Introduction to Objects 274 | ```js 275 | const xorausArray = [ 276 | 'Jordan', 277 | 'Ahmed', 278 | 2027 - 1997, 279 | 'teacher', 280 | ['Ali', 'Khan', 'Sal'] 281 | ]; 282 | 283 | const xoraus = { 284 | firstName: 'Jordan', 285 | lastName: 'Ahmed', 286 | age: 2027 - 1997, 287 | job: 'teacher', 288 | friends: ['Ali', 'K', 'Sal'] 289 | }; 290 | ``` 291 | 292 | ## 43. Dot vs. Bracket Notation 293 | ```js 294 | const xoraus = { 295 | firstName: 'Jordan', 296 | lastName: 'Ahmed', 297 | age: 2027 - 1997, 298 | job: 'teacher', 299 | friends: ['Plato', 'Aristotle', 'Socrates'] 300 | }; 301 | console.log(xoraus); 302 | 303 | console.log(xoraus.lastName); // output: Ahmed 304 | console.log(xoraus['lastName']); // output: Ahmed 305 | 306 | const nameKey = 'Name'; 307 | console.log(xoraus['first' + nameKey]); // output: Jordan 308 | console.log(xoraus['last' + nameKey]); // output: Ahmed 309 | 310 | // console.log(xoraus.'last' + nameKey) // this will not work 311 | 312 | const interestedIn = prompt('What do you want to know about Jordan? Choose between firstName, lastName, age, job, and friends'); 313 | 314 | if (xoraus[interestedIn]) { 315 | console.log(xoraus[interestedIn]); 316 | } else { 317 | console.log('Wrong request! Choose between firstName, lastName, age, job, and friends'); 318 | } 319 | 320 | xoraus.location = 'India'; 321 | xoraus['twitter'] = '@xoraus'; 322 | console.log(xoraus); 323 | 324 | // Challenge 325 | // "Jonas has 3 friends, and his best friend is called Aristotle" 326 | console.log(`${xoraus.firstName} has ${xoraus.friends.length} friends, and his best friend is called ${xoraus.friends[1]}`); 327 | 328 | ``` 329 | 330 | - when we need to first compute the property name, then we have to use the bracket notation otherwise use dot notation. 331 | - we get **undefined ** when when we try to access a property on an object that does not exist. 332 | 333 | ## 44. Object Methods 334 | ```js 335 | const xoraus = { 336 | firstName: 'Jordan', 337 | lastName: 'Ahmed', 338 | birthYeah: 1997, 339 | job: 'Programmer', 340 | friends: ['Socrates', 'Plato', 'Aristotle'], 341 | hasDriversLicense: true, 342 | 343 | // calcAge: function (birthYeah) { 344 | // return 2037 - birthYeah; 345 | // } 346 | 347 | // calcAge: function () { 348 | // // console.log(this); 349 | // return 2027 - this.birthYeah; 350 | // } 351 | 352 | calcAge: function () { 353 | this.age = 2027 - this.birthYeah; 354 | return this.age; 355 | }, 356 | 357 | getSummary: function () { 358 | return `${this.firstName} is a ${this.calcAge()}-year old ${xoraus.job}, and he has ${this.hasDriversLicense ? 'a' : 'no'} driver's license.` 359 | } 360 | }; 361 | 362 | console.log(xoraus.calcAge()); 363 | 364 | console.log(xoraus.age); 365 | console.log(xoraus.age); 366 | console.log(xoraus.age); 367 | 368 | // Challenge 369 | // "Jonas is a 30-year old programmer, and he has a driver's license" 370 | console.log(xoraus.getSummary()); 371 | ``` 372 | - Any function that is attached to an object is called a method. 373 | 374 | ## 45. Coding Challenge ##3 375 | Let's go back to Mark and John comparing their BMIs! This time, let's use objects to implement the calculations! Remember: BMI = mass / height ** 2 = mass / (height * height). (mass in kg and height in meter) 376 | 377 | 1. For each of them, create an object with properties for their full name, mass, and height (Mark Miller and John Smith) 378 | 2. Create a 'calcBMI' method on each object to calculate the BMI (the same method on both objects). Store the BMI value to a property, and also return it from the method. 379 | 3. Log to the console who has the higher BMI, together with the full name and the respective BMI. Example: "John Smith's BMI (28.3) is higher than Mark Miller's (23.9)!" 380 | 381 | TEST DATA: Marks weights 78 kg and is 1.69 m tall. John weights 92 kg and is 1.95 m tall. 382 | 383 | ```js 384 | const mark = { 385 | fullName: 'Mark Miller', 386 | mass: 78, 387 | height: 1.69, 388 | calcBMI: function () { 389 | this.bmi = this.mass / this.height ** 2; 390 | return this.bmi; 391 | } 392 | }; 393 | 394 | const john = { 395 | fullName: 'John Smith', 396 | mass: 92, 397 | height: 1.95, 398 | calcBMI: function () { 399 | this.bmi = this.mass / this.height ** 2; 400 | return this.bmi; 401 | } 402 | }; 403 | 404 | mark.calcBMI(); 405 | john.calcBMI(); 406 | 407 | console.log(mark.bmi, john.bmi); 408 | 409 | // "John Smith's BMI (28.3) is higher than Mark Miller's (23.9)!" 410 | 411 | if (mark.bmi > john.bmi) { 412 | console.log(`${mark.fullName}'s BMI (${mark.bmi}) is higher than ${john.fullName}'s BMI (${john.bmi})`) 413 | } else if (john.bmi > mark.bmi) { 414 | console.log(`${john.fullName}'s BMI (${john.bmi}) is higher than ${mark.fullName}'s BMI (${mark.bmi})`) 415 | } 416 | 417 | ``` 418 | 419 | ## 46. Iteration: The for Loop 420 | ```js 421 | // console.log('Lifting weights repetition 1 🏋️‍♀️'); 422 | // console.log('Lifting weights repetition 2 🏋️‍♀️'); 423 | // console.log('Lifting weights repetition 3 🏋️‍♀️'); 424 | // console.log('Lifting weights repetition 4 🏋️‍♀️'); 425 | // console.log('Lifting weights repetition 5 🏋️‍♀️'); 426 | // console.log('Lifting weights repetition 6 🏋️‍♀️'); 427 | // console.log('Lifting weights repetition 7 🏋️‍♀️'); 428 | // console.log('Lifting weights repetition 8 🏋️‍♀️'); 429 | // console.log('Lifting weights repetition 9 🏋️‍♀️'); 430 | // console.log('Lifting weights repetition 10 🏋️‍♀️'); 431 | 432 | // for loop keeps running while condition is TRUE 433 | for (let rep = 1; rep <= 30; rep++) { 434 | console.log(`Lifting weights repetition ${rep} 🏋️‍♀️`); 435 | } 436 | ``` 437 | 438 | ## 47. Looping Arrays, Breaking and Continuing 439 | ```js 440 | const jonas = [ 441 | 'Jonas', 442 | 'Schmedtmann', 443 | 2037 - 1991, 444 | 'teacher', 445 | ['Michael', 'Peter', 'Steven'], 446 | true 447 | ]; 448 | const types = []; 449 | 450 | // console.log(jonas[0]) 451 | // console.log(jonas[1]) 452 | // ... 453 | // console.log(jonas[4]) 454 | // jonas[5] does NOT exist 455 | 456 | for (let i = 0; i < jonas.length; i++) { 457 | // Reading from jonas array 458 | console.log(jonas[i], typeof jonas[i]); 459 | 460 | // Filling types array 461 | // types[i] = typeof jonas[i]; 462 | types.push(typeof jonas[i]); 463 | } 464 | 465 | console.log(types); 466 | 467 | const years = [1991, 2007, 1969, 2020]; 468 | const ages = []; 469 | 470 | for (let i = 0; i < years.length; i++) { 471 | ages.push(2037 - years[i]); 472 | } 473 | console.log(ages); 474 | 475 | // continue and break 476 | console.log('--- ONLY STRINGS ---') 477 | for (let i = 0; i < jonas.length; i++) { 478 | if (typeof jonas[i] !== 'string') continue; 479 | 480 | console.log(jonas[i], typeof jonas[i]); 481 | } 482 | 483 | console.log('--- BREAK WITH NUMBER ---') 484 | for (let i = 0; i < jonas.length; i++) { 485 | if (typeof jonas[i] === 'number') break; 486 | 487 | console.log(jonas[i], typeof jonas[i]); 488 | } 489 | 490 | ``` 491 | 492 | ## 48. Looping Backwards and Loops in Loops 493 | ```js 494 | const jonas = [ 495 | 'Jonas', 496 | 'Schmedtmann', 497 | 2037 - 1991, 498 | 'teacher', 499 | ['Michael', 'Peter', 'Steven'], 500 | true 501 | ]; 502 | 503 | // 0, 1, ..., 4 504 | // 4, 3, ..., 0 505 | 506 | for (let i = jonas.length - 1; i >= 0; i--) { 507 | console.log(i, jonas[i]); 508 | } 509 | 510 | for (let exercise = 1; exercise < 4; exercise++) { 511 | console.log(`-------- Starting exercise ${exercise}`); 512 | 513 | for (let rep = 1; rep < 6; rep++) { 514 | console.log(`Exercise ${exercise}: Lifting weight repetition ${rep} 🏋️‍♀️`); 515 | } 516 | } 517 | ``` 518 | 519 | ## 49. The while Loop 520 | ```js 521 | for (let rep = 1; rep <= 10; rep++) { 522 | console.log(`Lifting weights repetition ${rep} 🏋️‍♀️`); 523 | } 524 | 525 | let rep = 1; 526 | while (rep <= 10) { 527 | // console.log(`WHILE: Lifting weights repetition ${rep} 🏋️‍♀️`); 528 | rep++; 529 | } 530 | 531 | let dice = Math.trunc(Math.random() * 6) + 1; 532 | 533 | while (dice !== 6) { 534 | console.log(`You rolled a ${dice}`); 535 | dice = Math.trunc(Math.random() * 6) + 1; 536 | if (dice === 6) console.log('Loop is about to end...'); 537 | } 538 | ``` 539 | 540 | ## 50. Coding Challenge ##4 541 | 542 | Let's improve Steven's tip calculator even more, this time using loops! 543 | 1. Create an array 'bills' containing all 10 test bill values 544 | 2. Create empty arrays for the tips and the totals ('tips' and 'totals') 545 | 3. Use the 'calcTip' function we wrote before (no need to repeat) to calculate tips and total values (bill + tip) for every bill value in the bills array. Use a for loop to perform the 10 calculations! 546 | 547 | TEST DATA: 22, 295, 176, 440, 37, 105, 10, 1100, 86 and 52 548 | 549 | HINT: Call calcTip in the loop and use the push method to add values to the tips and totals arrays 😉 550 | 551 | 1. BONUS: Write a function 'calcAverage' which takes an array called 'arr' as an argument. This function calculates the average of all numbers in the given array. This is a DIFFICULT challenge (we haven't done this before)! Here is how to solve it: 552 | 4.1. First, you will need to add up all values in the array. To do the addition, start by creating a variable 'sum' that starts at 0. Then loop over the array using a for loop. In each iteration, add the current value to the 'sum' variable. This way, by the end of the loop, you have all values added together 553 | 4.2. To calculate the average, divide the sum you calculated before by the length of the array (because that's the number of elements) 554 | 4.3. Call the function with the 'totals' array 555 | 556 | ```js 557 | const calcTip = function (bill) { 558 | return bill >= 50 && bill <= 300 ? bill * 0.15 : bill * 0.2; 559 | } 560 | const bills = [22, 295, 176, 440, 37, 105, 10, 1100, 86, 52]; 561 | const tips = []; 562 | const totals = []; 563 | 564 | for (let i = 0; i < bills.length; i++) { 565 | const tip = calcTip(bills[i]); 566 | tips.push(tip); 567 | totals.push(tip + bills[i]); 568 | } 569 | console.log(bills, tips, totals); 570 | 571 | const calcAverage = function (arr) { 572 | let sum = 0; 573 | for (let i = 0; i < arr.length; i++) { 574 | // sum = sum + arr[i]; 575 | sum += arr[i]; 576 | } 577 | return sum / arr.length; 578 | } 579 | console.log(calcAverage([2, 3, 7])); 580 | console.log(calcAverage(totals)); 581 | console.log(calcAverage(tips)); 582 | ``` -------------------------------------------------------------------------------- /11. Numbers, Dates, Intl and Timers.md: -------------------------------------------------------------------------------- 1 | # 11. Numbers, Dates, Intl and Timers 2 | --- 3 | ## Converting and Checking Numbers 4 | ```js 5 | /////////////////////////////////////// 6 | // Converting and Checking Numbers 7 | console.log(23 === 23.0); 8 | 9 | // Base 10 - 0 to 9. 1/10 = 0.1. 3/10 = 3.3333333 10 | // Binary base 2 - 0 1 11 | console.log(0.1 + 0.2); 12 | console.log(0.1 + 0.2 === 0.3); 13 | 14 | // Conversion 15 | console.log(Number('23')); 16 | console.log(+'23'); 17 | 18 | // Parsing 19 | console.log(Number.parseInt('30px', 10)); 20 | console.log(Number.parseInt('e23', 10)); 21 | 22 | console.log(Number.parseInt(' 2.5rem ')); 23 | console.log(Number.parseFloat(' 2.5rem ')); 24 | 25 | // console.log(parseFloat(' 2.5rem ')); 26 | 27 | // Check if value is NaN 28 | console.log(Number.isNaN(20)); 29 | console.log(Number.isNaN('20')); 30 | console.log(Number.isNaN(+'20X')); 31 | console.log(Number.isNaN(23 / 0)); 32 | 33 | // Checking if value is number 34 | console.log(Number.isFinite(20)); 35 | console.log(Number.isFinite('20')); 36 | console.log(Number.isFinite(+'20X')); 37 | console.log(Number.isFinite(23 / 0)); 38 | 39 | console.log(Number.isInteger(23)); 40 | console.log(Number.isInteger(23.0)); 41 | console.log(Number.isInteger(23 / 0)); 42 | ``` 43 | - `Number.parseInt()` Number here provides something called "Namespace". 44 | 45 | ## Math and Rounding 46 | ```js 47 | /////////////////////////////////////// 48 | // Math and Rounding 49 | console.log(Math.sqrt(25)); 50 | console.log(25 ** (1 / 2)); 51 | console.log(8 ** (1 / 3)); 52 | 53 | console.log(Math.max(5, 18, 23, 11, 2)); 54 | console.log(Math.max(5, 18, '23', 11, 2)); 55 | console.log(Math.max(5, 18, '23px', 11, 2)); 56 | 57 | console.log(Math.min(5, 18, 23, 11, 2)); 58 | 59 | console.log(Math.PI * Number.parseFloat('10px') ** 2); 60 | 61 | console.log(Math.trunc(Math.random() * 6) + 1); 62 | 63 | const randomInt = (min, max) => 64 | Math.floor(Math.random() * (max - min) + 1) + min; 65 | // 0...1 -> 0...(max - min) -> min...max 66 | // console.log(randomInt(10, 20)); 67 | 68 | // Rounding integers 69 | console.log(Math.round(23.3)); 70 | console.log(Math.round(23.9)); 71 | 72 | console.log(Math.ceil(23.3)); 73 | console.log(Math.ceil(23.9)); 74 | 75 | console.log(Math.floor(23.3)); 76 | console.log(Math.floor('23.9')); 77 | 78 | console.log(Math.trunc(23.3)); 79 | 80 | console.log(Math.trunc(-23.3)); // -23 81 | console.log(Math.floor(-23.3)); // -24 82 | 83 | // Rounding decimals 84 | console.log((2.7).toFixed(0)); 85 | console.log((2.7).toFixed(3)); 86 | console.log((2.345).toFixed(2)); 87 | console.log(+(2.345).toFixed(2)); 88 | ``` 89 | - max function here actually does type coercion. 90 | - `floor()` is little bit better than `trunc()` because it works in all cases no matter if we are dealing with positive or negative numbers. 91 | - Primitives actually don't have methods. And so behind the scenes, JavaScript will do boxing. Boxing is to basically transform this to a number object, then call the method on that object. And then once the operation is finished it will convert it back to a primitive. 92 | 93 | ## The Remainder Operator 94 | ```js 95 | /////////////////////////////////////// 96 | // The Remainder Operator 97 | console.log(5 % 2); 98 | console.log(5 / 2); // 5 = 2 * 2 + 1 99 | 100 | console.log(8 % 3); 101 | console.log(8 / 3); // 8 = 2 * 3 + 2 102 | 103 | console.log(6 % 2); 104 | console.log(6 / 2); 105 | 106 | console.log(7 % 2); 107 | console.log(7 / 2); 108 | 109 | const isEven = n => n % 2 === 0; 110 | console.log(isEven(8)); 111 | console.log(isEven(23)); 112 | console.log(isEven(514)); 113 | 114 | labelBalance.addEventListener('click', function () { 115 | [...document.querySelectorAll('.movements__row')].forEach(function (row, i) { 116 | // 0, 2, 4, 6 117 | if (i % 2 === 0) row.style.backgroundColor = 'orangered'; 118 | // 0, 3, 6, 9 119 | if (i % 3 === 0) row.style.backgroundColor = 'blue'; 120 | }); 121 | }); 122 | ``` 123 | 124 | ## Numeric Separators - ES2021 125 | ```js 126 | /////////////////////////////////////// 127 | // Numeric Separators 128 | 129 | // 287,460,000,000 130 | const diameter = 287_460_000_000; 131 | console.log(diameter); 132 | 133 | const price = 345_99; 134 | console.log(price); 135 | 136 | const transferFee1 = 15_00; 137 | const transferFee2 = 1_500; 138 | 139 | const PI = 3.1415; 140 | console.log(PI); 141 | 142 | console.log(Number('230_000')); 143 | console.log(parseInt('230_000')); 144 | 145 | ``` 146 | 147 | ## Working with BigInt - ES2020 148 | - The `BigInt` is a new primitive type that can represent whole numbers bigger than `253 - 1`, which is the largest number Javascript can reliably represent with the `number` type. 149 | - Append `n` to a literal integer or use `BigInt()` function to create a `bigint`. 150 | ```js 151 | /////////////////////////////////////// 152 | // Working with BigInt 153 | console.log(2 ** 53 - 1); 154 | console.log(Number.MAX_SAFE_INTEGER); 155 | console.log(2 ** 53 + 1); 156 | console.log(2 ** 53 + 2); 157 | console.log(2 ** 53 + 3); 158 | console.log(2 ** 53 + 4); 159 | 160 | console.log(4838430248342043823408394839483204n); 161 | console.log(BigInt(48384302)); 162 | 163 | // Operations 164 | console.log(10000n + 10000n); 165 | console.log(36286372637263726376237263726372632n * 10000000n); 166 | // console.log(Math.sqrt(16n)); 167 | 168 | const huge = 20289830237283728378237n; 169 | const num = 23; 170 | console.log(huge * BigInt(num)); 171 | 172 | // Exceptions 173 | console.log(20n > 15); 174 | console.log(20n === 20); 175 | console.log(typeof 20n); 176 | console.log(20n == '20'); 177 | 178 | console.log(huge + ' is REALLY big!!!'); 179 | 180 | // Divisions 181 | console.log(11n / 3n); 182 | console.log(10 / 3); 183 | ``` 184 | 185 | ## Creating Dates 186 | ```js 187 | /////////////////////////////////////// 188 | // Creating Dates 189 | 190 | // Create a date 191 | 192 | const now = new Date(); 193 | console.log(now); 194 | 195 | console.log(new Date('Aug 02 2020 18:05:41')); 196 | console.log(new Date('December 24, 2015')); 197 | console.log(new Date(account1.movementsDates[0])); 198 | 199 | console.log(new Date(2037, 10, 19, 15, 23, 5)); 200 | console.log(new Date(2037, 10, 31)); 201 | 202 | console.log(new Date(0)); 203 | console.log(new Date(3 * 24 * 60 * 60 * 1000)); 204 | 205 | 206 | // Working with dates 207 | const future = new Date(2037, 10, 19, 15, 23); 208 | console.log(future); 209 | console.log(future.getFullYear()); 210 | console.log(future.getMonth()); 211 | console.log(future.getDate()); 212 | console.log(future.getDay()); 213 | console.log(future.getHours()); 214 | console.log(future.getMinutes()); 215 | console.log(future.getSeconds()); 216 | console.log(future.toISOString()); 217 | console.log(future.getTime()); 218 | 219 | console.log(new Date(2142256980000)); 220 | 221 | console.log(Date.now()); 222 | 223 | future.setFullYear(2040); 224 | console.log(future); 225 | ``` 226 | 227 | ## Operations With Dates 228 | ```js 229 | /////////////////////////////////////// 230 | // Operations With Dates 231 | const future = new Date(2037, 10, 19, 15, 23); 232 | console.log(+future); 233 | 234 | const calcDaysPassed = (date1, date2) => 235 | Math.abs(date2 - date1) / (1000 * 60 * 60 * 24); 236 | 237 | const days1 = calcDaysPassed(new Date(2037, 3, 4), new Date(2037, 3, 14)); 238 | console.log(days1); 239 | 240 | 241 | /////////////////////////////////////// 242 | // Internationalizing Numbers (Intl) 243 | const num = 3884764.23; 244 | 245 | const options = { 246 | style: 'currency', 247 | unit: 'celsius', 248 | currency: 'EUR', 249 | // useGrouping: false, 250 | }; 251 | 252 | console.log('US: ', new Intl.NumberFormat('en-US', options).format(num)); 253 | console.log('Germany: ', new Intl.NumberFormat('de-DE', options).format(num)); 254 | console.log('Syria: ', new Intl.NumberFormat('ar-SY', options).format(num)); 255 | console.log( 256 | navigator.language, 257 | new Intl.NumberFormat(navigator.language, options).format(num) 258 | ); 259 | ``` 260 | 261 | ## Internationalizing Numbers (Intl) 262 | ```js 263 | /////////////////////////////////////// 264 | // Internationalizing Numbers (Intl) 265 | const num = 3884764.23; 266 | 267 | const options = { 268 | style: 'currency', 269 | unit: 'celsius', 270 | currency: 'EUR', 271 | // useGrouping: false, 272 | }; 273 | 274 | console.log('US: ', new Intl.NumberFormat('en-US', options).format(num)); 275 | console.log('Germany: ', new Intl.NumberFormat('de-DE', options).format(num)); 276 | console.log('Syria: ', new Intl.NumberFormat('ar-SY', options).format(num)); 277 | console.log( 278 | navigator.language, 279 | new Intl.NumberFormat(navigator.language, options).format(num) 280 | ); 281 | 282 | ``` 283 | 284 | ## Timers 285 | ```js 286 | /////////////////////////////////////// 287 | // Timers 288 | 289 | // setTimeout 290 | const ingredients = ['olives', 'spinach']; 291 | const pizzaTimer = setTimeout( 292 | (ing1, ing2) => console.log(`Here is your pizza with ${ing1} and ${ing2} 🍕`), 293 | 3000, 294 | ...ingredients 295 | ); 296 | console.log('Waiting...'); 297 | 298 | if (ingredients.includes('spinach')) clearTimeout(pizzaTimer); 299 | 300 | // setInterval 301 | setInterval(function () { 302 | const now = new Date(); 303 | console.log(now); 304 | }, 1000); 305 | ``` 306 | 307 | ## Project Bankist Code 308 | ```js 309 | 'use strict'; 310 | 311 | ///////////////////////////////////////////////// 312 | ///////////////////////////////////////////////// 313 | // BANKIST APP 314 | 315 | ///////////////////////////////////////////////// 316 | // Data 317 | 318 | // DIFFERENT DATA! Contains movement dates, currency and locale 319 | 320 | const account1 = { 321 | owner: 'Jonas Schmedtmann', 322 | movements: [200, 455.23, -306.5, 25000, -642.21, -133.9, 79.97, 1300], 323 | interestRate: 1.2, // % 324 | pin: 1111, 325 | 326 | movementsDates: [ 327 | '2019-11-18T21:31:17.178Z', 328 | '2019-12-23T07:42:02.383Z', 329 | '2020-01-28T09:15:04.904Z', 330 | '2020-04-01T10:17:24.185Z', 331 | '2020-05-08T14:11:59.604Z', 332 | '2020-07-26T17:01:17.194Z', 333 | '2020-07-28T23:36:17.929Z', 334 | '2020-08-01T10:51:36.790Z', 335 | ], 336 | currency: 'EUR', 337 | locale: 'pt-PT', // de-DE 338 | }; 339 | 340 | const account2 = { 341 | owner: 'Jessica Davis', 342 | movements: [5000, 3400, -150, -790, -3210, -1000, 8500, -30], 343 | interestRate: 1.5, 344 | pin: 2222, 345 | 346 | movementsDates: [ 347 | '2019-11-01T13:15:33.035Z', 348 | '2019-11-30T09:48:16.867Z', 349 | '2019-12-25T06:04:23.907Z', 350 | '2020-01-25T14:18:46.235Z', 351 | '2020-02-05T16:33:06.386Z', 352 | '2020-04-10T14:43:26.374Z', 353 | '2020-06-25T18:49:59.371Z', 354 | '2020-07-26T12:01:20.894Z', 355 | ], 356 | currency: 'USD', 357 | locale: 'en-US', 358 | }; 359 | 360 | const accounts = [account1, account2]; 361 | 362 | ///////////////////////////////////////////////// 363 | // Elements 364 | const labelWelcome = document.querySelector('.welcome'); 365 | const labelDate = document.querySelector('.date'); 366 | const labelBalance = document.querySelector('.balance__value'); 367 | const labelSumIn = document.querySelector('.summary__value--in'); 368 | const labelSumOut = document.querySelector('.summary__value--out'); 369 | const labelSumInterest = document.querySelector('.summary__value--interest'); 370 | const labelTimer = document.querySelector('.timer'); 371 | 372 | const containerApp = document.querySelector('.app'); 373 | const containerMovements = document.querySelector('.movements'); 374 | 375 | const btnLogin = document.querySelector('.login__btn'); 376 | const btnTransfer = document.querySelector('.form__btn--transfer'); 377 | const btnLoan = document.querySelector('.form__btn--loan'); 378 | const btnClose = document.querySelector('.form__btn--close'); 379 | const btnSort = document.querySelector('.btn--sort'); 380 | 381 | const inputLoginUsername = document.querySelector('.login__input--user'); 382 | const inputLoginPin = document.querySelector('.login__input--pin'); 383 | const inputTransferTo = document.querySelector('.form__input--to'); 384 | const inputTransferAmount = document.querySelector('.form__input--amount'); 385 | const inputLoanAmount = document.querySelector('.form__input--loan-amount'); 386 | const inputCloseUsername = document.querySelector('.form__input--user'); 387 | const inputClosePin = document.querySelector('.form__input--pin'); 388 | 389 | ///////////////////////////////////////////////// 390 | // Functions 391 | 392 | const formatMovementDate = function (date, locale) { 393 | const calcDaysPassed = (date1, date2) => 394 | Math.round(Math.abs(date2 - date1) / (1000 * 60 * 60 * 24)); 395 | 396 | const daysPassed = calcDaysPassed(new Date(), date); 397 | console.log(daysPassed); 398 | 399 | if (daysPassed === 0) return 'Today'; 400 | if (daysPassed === 1) return 'Yesterday'; 401 | if (daysPassed <= 7) return `${daysPassed} days ago`; 402 | 403 | // const day = `${date.getDate()}`.padStart(2, 0); 404 | // const month = `${date.getMonth() + 1}`.padStart(2, 0); 405 | // const year = date.getFullYear(); 406 | // return `${day}/${month}/${year}`; 407 | return new Intl.DateTimeFormat(locale).format(date); 408 | }; 409 | 410 | const formatCur = function (value, locale, currency) { 411 | return new Intl.NumberFormat(locale, { 412 | style: 'currency', 413 | currency: currency, 414 | }).format(value); 415 | }; 416 | 417 | const displayMovements = function (acc, sort = false) { 418 | containerMovements.innerHTML = ''; 419 | 420 | const movs = sort 421 | ? acc.movements.slice().sort((a, b) => a - b) 422 | : acc.movements; 423 | 424 | movs.forEach(function (mov, i) { 425 | const type = mov > 0 ? 'deposit' : 'withdrawal'; 426 | 427 | const date = new Date(acc.movementsDates[i]); 428 | const displayDate = formatMovementDate(date, acc.locale); 429 | 430 | const formattedMov = formatCur(mov, acc.locale, acc.currency); 431 | 432 | const html = ` 433 |
434 |
${ 435 | i + 1 436 | } ${type}
437 |
${displayDate}
438 |
${formattedMov}
439 |
440 | `; 441 | 442 | containerMovements.insertAdjacentHTML('afterbegin', html); 443 | }); 444 | }; 445 | 446 | const calcDisplayBalance = function (acc) { 447 | acc.balance = acc.movements.reduce((acc, mov) => acc + mov, 0); 448 | labelBalance.textContent = formatCur(acc.balance, acc.locale, acc.currency); 449 | }; 450 | 451 | const calcDisplaySummary = function (acc) { 452 | const incomes = acc.movements 453 | .filter(mov => mov > 0) 454 | .reduce((acc, mov) => acc + mov, 0); 455 | labelSumIn.textContent = formatCur(incomes, acc.locale, acc.currency); 456 | 457 | const out = acc.movements 458 | .filter(mov => mov < 0) 459 | .reduce((acc, mov) => acc + mov, 0); 460 | labelSumOut.textContent = formatCur(Math.abs(out), acc.locale, acc.currency); 461 | 462 | const interest = acc.movements 463 | .filter(mov => mov > 0) 464 | .map(deposit => (deposit * acc.interestRate) / 100) 465 | .filter((int, i, arr) => { 466 | // console.log(arr); 467 | return int >= 1; 468 | }) 469 | .reduce((acc, int) => acc + int, 0); 470 | labelSumInterest.textContent = formatCur(interest, acc.locale, acc.currency); 471 | }; 472 | 473 | const createUsernames = function (accs) { 474 | accs.forEach(function (acc) { 475 | acc.username = acc.owner 476 | .toLowerCase() 477 | .split(' ') 478 | .map(name => name[0]) 479 | .join(''); 480 | }); 481 | }; 482 | createUsernames(accounts); 483 | 484 | const updateUI = function (acc) { 485 | // Display movements 486 | displayMovements(acc); 487 | 488 | // Display balance 489 | calcDisplayBalance(acc); 490 | 491 | // Display summary 492 | calcDisplaySummary(acc); 493 | }; 494 | 495 | const startLogOutTimer = function () { 496 | const tick = function () { 497 | const min = String(Math.trunc(time / 60)).padStart(2, 0); 498 | const sec = String(time % 60).padStart(2, 0); 499 | 500 | // In each call, print the remaining time to UI 501 | labelTimer.textContent = `${min}:${sec}`; 502 | 503 | // When 0 seconds, stop timer and log out user 504 | if (time === 0) { 505 | clearInterval(timer); 506 | labelWelcome.textContent = 'Log in to get started'; 507 | containerApp.style.opacity = 0; 508 | } 509 | 510 | // Decrease 1s 511 | time--; 512 | }; 513 | 514 | // Set time to 5 minutes 515 | let time = 120; 516 | 517 | // Call the timer every second 518 | tick(); 519 | const timer = setInterval(tick, 1000); 520 | 521 | return timer; 522 | }; 523 | 524 | /////////////////////////////////////// 525 | // Event handlers 526 | let currentAccount, timer; 527 | 528 | // FAKE ALWAYS LOGGED IN 529 | // currentAccount = account1; 530 | // updateUI(currentAccount); 531 | // containerApp.style.opacity = 100; 532 | 533 | btnLogin.addEventListener('click', function (e) { 534 | // Prevent form from submitting 535 | e.preventDefault(); 536 | 537 | currentAccount = accounts.find( 538 | acc => acc.username === inputLoginUsername.value 539 | ); 540 | console.log(currentAccount); 541 | 542 | if (currentAccount?.pin === +inputLoginPin.value) { 543 | // Display UI and message 544 | labelWelcome.textContent = `Welcome back, ${ 545 | currentAccount.owner.split(' ')[0] 546 | }`; 547 | containerApp.style.opacity = 100; 548 | 549 | // Create current date and time 550 | const now = new Date(); 551 | const options = { 552 | hour: 'numeric', 553 | minute: 'numeric', 554 | day: 'numeric', 555 | month: 'numeric', 556 | year: 'numeric', 557 | // weekday: 'long', 558 | }; 559 | // const locale = navigator.language; 560 | // console.log(locale); 561 | 562 | labelDate.textContent = new Intl.DateTimeFormat( 563 | currentAccount.locale, 564 | options 565 | ).format(now); 566 | 567 | // const day = `${now.getDate()}`.padStart(2, 0); 568 | // const month = `${now.getMonth() + 1}`.padStart(2, 0); 569 | // const year = now.getFullYear(); 570 | // const hour = `${now.getHours()}`.padStart(2, 0); 571 | // const min = `${now.getMinutes()}`.padStart(2, 0); 572 | // labelDate.textContent = `${day}/${month}/${year}, ${hour}:${min}`; 573 | 574 | // Clear input fields 575 | inputLoginUsername.value = inputLoginPin.value = ''; 576 | inputLoginPin.blur(); 577 | 578 | // Timer 579 | if (timer) clearInterval(timer); 580 | timer = startLogOutTimer(); 581 | 582 | // Update UI 583 | updateUI(currentAccount); 584 | } 585 | }); 586 | 587 | btnTransfer.addEventListener('click', function (e) { 588 | e.preventDefault(); 589 | const amount = +inputTransferAmount.value; 590 | const receiverAcc = accounts.find( 591 | acc => acc.username === inputTransferTo.value 592 | ); 593 | inputTransferAmount.value = inputTransferTo.value = ''; 594 | 595 | if ( 596 | amount > 0 && 597 | receiverAcc && 598 | currentAccount.balance >= amount && 599 | receiverAcc?.username !== currentAccount.username 600 | ) { 601 | // Doing the transfer 602 | currentAccount.movements.push(-amount); 603 | receiverAcc.movements.push(amount); 604 | 605 | // Add transfer date 606 | currentAccount.movementsDates.push(new Date().toISOString()); 607 | receiverAcc.movementsDates.push(new Date().toISOString()); 608 | 609 | // Update UI 610 | updateUI(currentAccount); 611 | 612 | // Reset timer 613 | clearInterval(timer); 614 | timer = startLogOutTimer(); 615 | } 616 | }); 617 | 618 | btnLoan.addEventListener('click', function (e) { 619 | e.preventDefault(); 620 | 621 | const amount = Math.floor(inputLoanAmount.value); 622 | 623 | if (amount > 0 && currentAccount.movements.some(mov => mov >= amount * 0.1)) { 624 | setTimeout(function () { 625 | // Add movement 626 | currentAccount.movements.push(amount); 627 | 628 | // Add loan date 629 | currentAccount.movementsDates.push(new Date().toISOString()); 630 | 631 | // Update UI 632 | updateUI(currentAccount); 633 | 634 | // Reset timer 635 | clearInterval(timer); 636 | timer = startLogOutTimer(); 637 | }, 2500); 638 | } 639 | inputLoanAmount.value = ''; 640 | }); 641 | 642 | btnClose.addEventListener('click', function (e) { 643 | e.preventDefault(); 644 | 645 | if ( 646 | inputCloseUsername.value === currentAccount.username && 647 | +inputClosePin.value === currentAccount.pin 648 | ) { 649 | const index = accounts.findIndex( 650 | acc => acc.username === currentAccount.username 651 | ); 652 | console.log(index); 653 | // .indexOf(23) 654 | 655 | // Delete account 656 | accounts.splice(index, 1); 657 | 658 | // Hide UI 659 | containerApp.style.opacity = 0; 660 | } 661 | 662 | inputCloseUsername.value = inputClosePin.value = ''; 663 | }); 664 | 665 | let sorted = false; 666 | btnSort.addEventListener('click', function (e) { 667 | e.preventDefault(); 668 | displayMovements(currentAccount.movements, !sorted); 669 | sorted = !sorted; 670 | }); 671 | ``` -------------------------------------------------------------------------------- /13. Object-Oriented Programming (OOP) .md: -------------------------------------------------------------------------------- 1 | # 13. Object-Oriented Programming (O0P) With JavaScript 2 | --- 3 | ## What is Object Oriented Programming? 4 | ![[13. Object-Oriented Programming (O0P) With JavaScript-1663898980491.jpeg]] 5 | 6 | ### Classes and Instances (Traditional OOP) 7 | ![[13. Object-Oriented Programming (O0P) With JavaScript-1663899008964.jpeg]] 8 | 9 | ### The 4 Fundamental OOP Principle 10 | ![[13. Object-Oriented Programming (O0P) With JavaScript-1663899032649.jpeg]] 11 | 12 | ### Abstraction 13 | - **Abstraction**: Ignoring or hiding details that *don't matter*, allowing us to 14 | get _an overview_ perspective of the thing we're implementing, instead of 15 | messing with details that don't really matter to our implementation. 16 | ![[13. Object-Oriented Programming (O0P) With JavaScript-1663899059332.jpeg]] 17 | 18 | ### Encapsulation Inheritance 19 | - **Encapsulation**: Keeping properties and methods *private* inside the class, 20 | SO they are *not accessible from outside the class*. Some methods can 21 | be exposed as a public interface (API). 22 | ![[13. Object-Oriented Programming (O0P) With JavaScript-1663899150174.jpeg]] 23 | 24 | ### Polymorphism 25 | - **Inheritance**: Making all properties and methods of a certain class *available 26 | to a child class*, forming a hierarchical relationship between classes. This 27 | allows us to *reuse common logic* and to model real-world relationships. 28 | ![[13. Object-Oriented Programming (O0P) With JavaScript-1663899193652.jpeg]] 29 | 30 | ## 00P in JavaScript 31 | - Polymorphism: A child class can *overwrite* a method it inherited from a 32 | parent class [it's more complex that that, but enough for our purposes]. 33 | ![[13. Object-Oriented Programming (O0P) With JavaScript-1663899232684.jpeg]] 34 | 35 | ## 00P in JavaScript 36 | ![[13. Object-Oriented Programming (O0P) With JavaScript-1663899253131.jpeg]] 37 | 38 | **3 Ways of Implementing Prototypical Inheritance** 39 | ![[13. Object-Oriented Programming (O0P) With JavaScript-1663899280934.jpeg]] 40 | 41 | ## Constructor Functions and the new Operator 42 | - The only difference between a regular function, and a function that we call constructor function, is that we call a constructor function with the new operator. 43 | - An arrow function will actually not work as a function constructor. And that's because it doesn't have its own this keyword 44 | - And constructor functions have been used since the beginning of JavaScript to kind of simulate classes. 45 | - Just note that function constructors are not really a feature of the JavaScript language. Instead, they are simply a pattern that has been developed by other developers. And now everyone simply uses this. 46 | ```js 47 | /* all the objects that are created through this constructor function here will inherit, so they will get access to all the methods and properties that are defined on this prototype property. */ 48 | const Person = function (firstName, birthYear) { 49 | // instance properties 50 | this.firstName = firstName; 51 | this.birthYear = birthYear; 52 | 53 | // You should never create a method inside of a constructor fucnction 54 | // this.calcAge = 2047 - this.birthYear; 55 | }; 56 | 57 | new Person('Ahmed', 1997); 58 | 59 | // 1. New {} is created 60 | // 2. function is called, this = {} 61 | // 3. {} is linked to prototype 62 | // 4. fucntion automatically return {} 63 | 64 | const ahmed = new Person('Ahmed', 1997); 65 | const salaria = new Person('Salaria', 1998); 66 | 67 | console.log(ahmed); 68 | console.log(salaria); 69 | 70 | console.log(ahmed instanceof Person); // true 71 | ``` 72 | 73 | ## Prototypes 74 | ```js 75 | // Prototypes 76 | console.log(Person.prototype); 77 | 78 | Person.prototype.calcAge = function () { 79 | console.log(2047 - this.birthYear); 80 | }; 81 | 82 | ahmed.calcAge(); 83 | salaria.calcAge(); 84 | 85 | console.log(ahmed.__proto__); 86 | 87 | // Where does this proto property comes from? From step number 3 88 | // 1. New {} is created 89 | // 2. function is called, this = {} 90 | // 3. {} is linked to prototype 91 | // 4. fucntion automatically return {} 92 | 93 | console.log(ahmed.__proto__ === Person.prototype); 94 | 95 | /* So person dot prototype here is actually not the prototype of person. But instead, it is what's gonna be used as the prototype of all the objects that are created with the person constructor function. 96 | So that's a subtle a but important difference that you need to keep in mind. And, in fact, what I just said that is confirmed by this comparison */ 97 | 98 | console.log(Person.prototype.isPrototypeOf(ahmed)); // true 99 | console.log(Person.prototype.isPrototypeOf(salaria)); // true 100 | console.log(Person.prototype.isPrototypeOf(Person)); // false 101 | 102 | // Rather than calling it prototype is should have been called .prototypeOfLinkedObjects 103 | 104 | Person.prototype.species = 'Homo Sapiens'; 105 | console.log(ahmed.species, salaria.species); 106 | 107 | console.log(ahmed.hasOwnPropery('firstName')); // true 108 | console.log(ahmed.hasOwnPropery('species')); // false 109 | ``` 110 | 111 | ## Prototypal Inheritance and The Prototype Chain 112 | **How Prototypical Inheritance / Delegation works?** 113 | ![[13. Object-Oriented Programming (O0P) With JavaScript-1663906658628.jpeg]] 114 | **The Prototype Chain** 115 | ![[13. Object-Oriented Programming (O0P) With JavaScript-1663906701353.jpeg]] 116 | 117 | ## Prototypal Inheritance on Built-In Objects 118 | ```js 119 | /////////////////////////////////////// 120 | // Prototypal Inheritance on Built-In Objects 121 | console.log(jonas.__proto__); 122 | // Object.prototype (top of prototype chain) 123 | console.log(jonas.__proto__.__proto__); 124 | console.log(jonas.__proto__.__proto__.__proto__); 125 | 126 | console.dir(Person.prototype.constructor); 127 | 128 | const arr = [3, 6, 6, 5, 6, 9, 9]; // new Array === [] 129 | console.log(arr.__proto__); 130 | console.log(arr.__proto__ === Array.prototype); 131 | 132 | console.log(arr.__proto__.__proto__); 133 | 134 | Array.prototype.unique = function () { 135 | return [...new Set(this)]; 136 | }; 137 | 138 | console.log(arr.unique()); 139 | 140 | const h1 = document.querySelector('h1'); 141 | console.dir(x => x + 1); 142 | ``` 143 | 144 | ## Coding Challenge #1 145 | 1. Use a constructor function to implement a Car. A car has a make and a speed property. The speed property is the current speed of the car in km/h; 146 | 2. Implement an 'accelerate' method that will increase the car's speed by 10, and log the new speed to the console; 147 | 3. Implement a 'brake' method that will decrease the car's speed by 5, and log the new speed to the console; 148 | 4. Create 2 car objects and experiment with calling 'accelerate' and 'brake' multiple times on each of them. 149 | 150 | DATA CAR 1: 'BMW' going at 120 km/h 151 | DATA CAR 2: 'Mercedes' going at 95 km/h 152 | 153 | ```js 154 | const Car = function (make, speed) { 155 | this.make = make; 156 | this.speed = speed; 157 | }; 158 | 159 | Car.prototype.accelerate = function () { 160 | this.speed += 10; 161 | console.log(`${this.make} is going at ${this.speed} km/h`); 162 | }; 163 | 164 | Car.prototype.brake = function () { 165 | this.speed -= 5; 166 | console.log(`${this.make} is going at ${this.speed} km/h`); 167 | }; 168 | 169 | const bmw = new Car('BMW', 120); 170 | const mercedes = new Car('Mercedes', 95); 171 | 172 | bmw.accelerate(); 173 | bmw.accelerate(); 174 | bmw.brake(); 175 | bmw.accelerate(); 176 | ``` 177 | 178 | ## ES6 Classes 179 | ```js 180 | /////////////////////////////////////// 181 | // ES6 Classes 182 | 183 | // Class expression 184 | // const PersonCl = class {} 185 | 186 | // Class declaration 187 | class PersonCl { 188 | constructor(fullName, birthYear) { 189 | this.fullName = fullName; 190 | this.birthYear = birthYear; 191 | } 192 | 193 | // Instance methods 194 | // Methods will be added to .prototype property 195 | calcAge() { 196 | console.log(2037 - this.birthYear); 197 | } 198 | 199 | greet() { 200 | console.log(`Hey ${this.fullName}`); 201 | } 202 | 203 | get age() { 204 | return 2037 - this.birthYear; 205 | } 206 | 207 | // Set a property that already exists 208 | set fullName(name) { 209 | if (name.includes(' ')) this._fullName = name; 210 | else alert(`${name} is not a full name!`); 211 | } 212 | 213 | get fullName() { 214 | return this._fullName; 215 | } 216 | 217 | ``` 218 | 219 | ## Setters and Getters 220 | - So every object in JavaScript can have setter and getter properties. And we call these special properties assessor properties, while the more normal properties are called data properties. So getters and setters are basically functions that get and set a value so just as the name says, but on the outside they still look like regular properties. 221 | ```js 222 | /////////////////////////////////////// 223 | // Setters and Getters 224 | const account = { 225 | owner: 'Jonas', 226 | movements: [200, 530, 120, 300], 227 | 228 | get latest() { 229 | return this.movements.slice(-1).pop(); 230 | }, 231 | 232 | set latest(mov) { 233 | this.movements.push(mov); 234 | }, 235 | }; 236 | 237 | console.log(account.latest); 238 | 239 | account.latest = 50; 240 | console.log(account.movements); 241 | ``` 242 | 243 | ## Static Methods 244 | ```js 245 | // Static method 246 | static hey() { 247 | console.log('Hey there 👋'); 248 | console.log(this); 249 | } 250 | } 251 | 252 | const jessica = new PersonCl('Jessica Davis', 1996); 253 | console.log(jessica); 254 | jessica.calcAge(); 255 | console.log(jessica.age); 256 | 257 | console.log(jessica.__proto__ === PersonCl.prototype); 258 | 259 | // PersonCl.prototype.greet = function () { 260 | // console.log(`Hey ${this.firstName}`); 261 | // }; 262 | jessica.greet(); 263 | 264 | // 1. Classes are NOT hoisted 265 | // 2. Classes are first-class citizens 266 | // 3. Classes are executed in strict mode 267 | 268 | const walter = new PersonCl('Walter White', 1965); 269 | // PersonCl.hey(); 270 | ``` 271 | 272 | ## Object.create 273 | ```js 274 | /////////////////////////////////////// 275 | // Object.create 276 | const PersonProto = { 277 | calcAge() { 278 | console.log(2037 - this.birthYear); 279 | }, 280 | 281 | init(firstName, birthYear) { 282 | this.firstName = firstName; 283 | this.birthYear = birthYear; 284 | }, 285 | }; 286 | 287 | const steven = Object.create(PersonProto); 288 | console.log(steven); 289 | steven.name = 'Steven'; 290 | steven.birthYear = 2002; 291 | steven.calcAge(); 292 | 293 | console.log(steven.__proto__ === PersonProto); 294 | 295 | const sarah = Object.create(PersonProto); 296 | sarah.init('Sarah', 1979); 297 | sarah.calcAge(); 298 | ``` 299 | 300 | ## Coding Challenge #2 301 | ```js 302 | class CarCl { 303 | constructor(make, speed) { 304 | this.make = make; 305 | this.speed = speed; 306 | } 307 | 308 | accelerate() { 309 | this.speed += 10; 310 | console.log(`${this.make} is going at ${this.speed} km/h`); 311 | } 312 | 313 | brake() { 314 | this.speed -= 5; 315 | console.log(`${this.make} is going at ${this.speed} km/h`); 316 | } 317 | 318 | get speedUS() { 319 | return this.speed / 1.6; 320 | } 321 | 322 | set speedUS(speed) { 323 | this.speed = speed * 1.6; 324 | } 325 | } 326 | 327 | const ford = new CarCl('Ford', 120); 328 | console.log(ford.speedUS); 329 | ford.accelerate(); 330 | ford.accelerate(); 331 | ford.brake(); 332 | ford.speedUS = 50; 333 | console.log(ford); 334 | ``` 335 | 336 | ## Inheritance Between "Classes": Constructor Functions 337 | ```js 338 | /////////////////////////////////////// 339 | // Inheritance Between "Classes": Constructor Functions 340 | 341 | const Person = function (firstName, birthYear) { 342 | this.firstName = firstName; 343 | this.birthYear = birthYear; 344 | }; 345 | 346 | Person.prototype.calcAge = function () { 347 | console.log(2037 - this.birthYear); 348 | }; 349 | 350 | const Student = function (firstName, birthYear, course) { 351 | Person.call(this, firstName, birthYear); 352 | this.course = course; 353 | }; 354 | 355 | // Linking prototypes 356 | Student.prototype = Object.create(Person.prototype); // At this point, student.prototype is empty 357 | 358 | Student.prototype.introduce = function () { 359 | console.log(`My name is ${this.firstName} and I study ${this.course}`); 360 | }; 361 | 362 | const mike = new Student('Mike', 2020, 'Computer Science'); 363 | mike.introduce(); 364 | mike.calcAge(); 365 | 366 | console.log(mike.__proto__); 367 | console.log(mike.__proto__.__proto__); 368 | 369 | console.log(mike instanceof Student); 370 | console.log(mike instanceof Person); 371 | console.log(mike instanceof Object); 372 | 373 | Student.prototype.constructor = Student; 374 | console.dir(Student.prototype.constructor); 375 | ``` 376 | 377 | ## Coding Challenge #3 378 | 1. Use a constructor function to implement an Electric Car (called EV) as a CHILD "class" of Car. Besides a make and current speed, the EV also has the current battery charge in % ('charge' property); 379 | 2. Implement a 'chargeBattery' method which takes an argument 'chargeTo' and sets the battery charge to 'chargeTo'; 380 | 3. Implement an 'accelerate' method that will increase the car's speed by 20, and decrease the charge by 1%. Then log a message like this: 'Tesla going at 140 km/h, with a charge of 22%'; 381 | 4. Create an electric car object and experiment with calling 'accelerate', 'brake' and 'chargeBattery' (charge to 90%). Notice what happens when you 'accelerate'! HINT: Review the definiton of polymorphism 😉 382 | 383 | DATA CAR 1: 'Tesla' going at 120 km/h, with a charge of 23% 384 | ```js 385 | const Car = function (make, speed) { 386 | this.make = make; 387 | this.speed = speed; 388 | }; 389 | 390 | Car.prototype.accelerate = function () { 391 | this.speed += 10; 392 | console.log(`${this.make} is going at ${this.speed} km/h`); 393 | }; 394 | 395 | Car.prototype.brake = function () { 396 | this.speed -= 5; 397 | console.log(`${this.make} is going at ${this.speed} km/h`); 398 | }; 399 | 400 | const EV = function (make, speed, charge) { 401 | Car.call(this, make, speed); 402 | this.charge = charge; 403 | }; 404 | 405 | // Link the prototypes 406 | EV.prototype = Object.create(Car.prototype); 407 | 408 | EV.prototype.chargeBattery = function (chargeTo) { 409 | this.charge = chargeTo; 410 | }; 411 | 412 | EV.prototype.accelerate = function () { 413 | this.speed += 20; 414 | this.charge--; 415 | console.log( 416 | `${this.make} is going at ${this.speed} km/h, with a charge of ${this.charge}` 417 | ); 418 | }; 419 | 420 | const tesla = new EV('Tesla', 120, 23); 421 | tesla.chargeBattery(90); 422 | console.log(tesla); 423 | tesla.brake(); 424 | tesla.accelerate(); 425 | ``` 426 | 427 | ## Inheritance Between "Classes": ES6 Classes 428 | ![[13. Object-Oriented Programming (O0P) With JavaScript-1663994719743.jpeg]] 429 | 430 | ![[13. Object-Oriented Programming (O0P) With JavaScript-1663994740330.jpeg]] 431 | 432 | ![[13. Object-Oriented Programming (O0P) With JavaScript-1663994769380.jpeg]] 433 | 434 | ![[13. Object-Oriented Programming (O0P) With JavaScript-1663994814589.jpeg]] 435 | 436 | ```js 437 | /////////////////////////////////////// 438 | // Inheritance Between "Classes": ES6 Classes 439 | 440 | class PersonCl { 441 | constructor(fullName, birthYear) { 442 | this.fullName = fullName; 443 | this.birthYear = birthYear; 444 | } 445 | 446 | // Instance methods 447 | calcAge() { 448 | console.log(2037 - this.birthYear); 449 | } 450 | 451 | greet() { 452 | console.log(`Hey ${this.fullName}`); 453 | } 454 | 455 | get age() { 456 | return 2037 - this.birthYear; 457 | } 458 | 459 | set fullName(name) { 460 | if (name.includes(' ')) this._fullName = name; 461 | else alert(`${name} is not a full name!`); 462 | } 463 | 464 | get fullName() { 465 | return this._fullName; 466 | } 467 | 468 | // Static method 469 | static hey() { 470 | console.log('Hey there 👋'); 471 | } 472 | } 473 | 474 | class StudentCl extends PersonCl { 475 | constructor(fullName, birthYear, course) { 476 | // Always needs to happen first! 477 | super(fullName, birthYear); 478 | this.course = course; 479 | } 480 | 481 | introduce() { 482 | console.log(`My name is ${this.fullName} and I study ${this.course}`); 483 | } 484 | 485 | calcAge() { 486 | console.log( 487 | `I'm ${ 488 | 2037 - this.birthYear 489 | } years old, but as a student I feel more like ${ 490 | 2037 - this.birthYear + 10 491 | }` 492 | ); 493 | } 494 | } 495 | 496 | const martha = new StudentCl('Martha Jones', 2012, 'Computer Science'); 497 | martha.introduce(); 498 | martha.calcAge(); 499 | ``` 500 | 501 | ## Inheritance Between "Classes": Object.create 502 | ![[13. Object-Oriented Programming (O0P) With JavaScript-1663994609934.jpeg]] 503 | 504 | ```js 505 | /////////////////////////////////////// 506 | // Inheritance Between "Classes": Object.create 507 | 508 | const PersonProto = { 509 | calcAge() { 510 | console.log(2037 - this.birthYear); 511 | }, 512 | 513 | init(firstName, birthYear) { 514 | this.firstName = firstName; 515 | this.birthYear = birthYear; 516 | }, 517 | }; 518 | 519 | const steven = Object.create(PersonProto); 520 | 521 | const StudentProto = Object.create(PersonProto); 522 | StudentProto.init = function (firstName, birthYear, course) { 523 | PersonProto.init.call(this, firstName, birthYear); 524 | this.course = course; 525 | }; 526 | 527 | StudentProto.introduce = function () { 528 | // BUG in video: 529 | // console.log(`My name is ${this.fullName} and I study ${this.course}`); 530 | 531 | // FIX: 532 | console.log(`My name is ${this.firstName} and I study ${this.course}`); 533 | }; 534 | 535 | const jay = Object.create(StudentProto); 536 | jay.init('Jay', 2010, 'Computer Science'); 537 | jay.introduce(); 538 | jay.calcAge(); 539 | ``` 540 | 541 | ## Another Class Example 542 | ```js 543 | class Account { 544 | constructor(owner, currency, pin) { 545 | this.owner = owner; 546 | this.currency = currency; 547 | this.pin = pin; 548 | this.movements = []; 549 | this.locale = navigator.language; 550 | console.log(`Thanks for opening an account ${this.name}`); 551 | } 552 | 553 | // Public Interface 554 | 555 | deposit(val) { 556 | this.movements.push(val); 557 | } 558 | withdraw(val) { 559 | this.deposit(-val); 560 | } 561 | approveLoan(val) { 562 | return true; 563 | } 564 | requestLoan(val) { 565 | if (this.approveLoan(val)) { 566 | this.deposit(val); 567 | console.log('Loan Approved'); 568 | } 569 | } 570 | } 571 | 572 | const acc1 = new Account('xoraus', 'RUP', 1111); 573 | 574 | acc1.deposit(250); 575 | acc1.deposit(140); 576 | acc1.requestLoan(1000); 577 | // acc1.approveLoan(1000); This method should only be available to the requestLoan method 578 | // Therefore we need data encapsulation and data privacy 579 | 580 | console.log(acc1); 581 | // console.log(acc1.pin); - This should not be accessible 582 | ``` 583 | 584 | ## Encapsulation: Protected Properties and Methods 585 | - Now first, remember that encapsulation basically means to keep some properties and methods private inside the class so that they are not accessible from outside of the class. Then the rest of the methods are basically exposed as a public interface, which we can also call API. 586 | - Now, there are two big reasons why we need encapsulation and data privacy. 587 | - So first it is to prevent code from outside of a class to accidentally manipulate or data inside the class. 588 | - Now, the second reason is that when we expose only a small interface so a small API consisting only of a few public methods then we can change all the other internal methods with more confidence. Because in this case, we can be sure that external code does not rely on these private methods. 589 | - Encapsulation means information hiding. It's about hiding as much as possible of the object's internal parts and exposing a minimal public interface. The simplest and most elegant way to create encapsulation in JavaScript is using closures. A closure can be created as a function with private state. 590 | 591 | ```js 592 | 'use strict'; 593 | 594 | class Account { 595 | constructor(owner, currency, pin) { 596 | this.owner = owner; 597 | this.currency = currency; 598 | 599 | // Protected Property 600 | this._pin = pin; 601 | this._movements = []; 602 | this.locale = navigator.language; 603 | console.log(`Thanks for opening an account ${this.name}`); 604 | } 605 | 606 | // Public Interface 607 | 608 | getMovements() { 609 | return this._movements; 610 | } 611 | 612 | deposit(val) { 613 | this._movements.push(val); 614 | } 615 | withdraw(val) { 616 | this.deposit(-val); 617 | } 618 | _approveLoan(val) { 619 | return true; 620 | } 621 | requestLoan(val) { 622 | if (this._approveLoan(val)) { 623 | this.deposit(val); 624 | console.log('Loan Approved'); 625 | } 626 | } 627 | } 628 | 629 | const acc1 = new Account('xoraus', 'RUP', 1111); 630 | 631 | // acc1._movements.push(250); 632 | // acc1._movements.push(-140); 633 | 634 | acc1.deposit(250); 635 | acc1.deposit(140); 636 | 637 | acc1.requestLoan(1000); 638 | // acc1.approveLoan(1000); This method should only be available to the requestLoan method 639 | // Therefore we need data encapsulation and data privacy 640 | 641 | console.log(acc1.getMovements()); 642 | 643 | console.log(acc1); 644 | // console.log(acc1.pin); - This should not be accessible 645 | 646 | ``` 647 | 648 | ## Encapsulation: Private Class Fields and Methods 649 | 650 | ```js 651 | /////////////////////////////////////// 652 | // Encapsulation: Protected Properties and Methods 653 | // Encapsulation: Private Class Fields and Methods 654 | 655 | // 1) Public fields 656 | // 2) Private fields 657 | // 3) Public methods 658 | // 4) Private methods 659 | // (there is also the static version) 660 | 661 | class Account { 662 | // 1) Public fields (instances) 663 | locale = navigator.language; 664 | 665 | // 2) Private fields (instances) 666 | #movements = []; 667 | #pin; 668 | 669 | constructor(owner, currency, pin) { 670 | this.owner = owner; 671 | this.currency = currency; 672 | this.#pin = pin; 673 | 674 | // Protected property 675 | // this._movements = []; 676 | // this.locale = navigator.language; 677 | 678 | console.log(`Thanks for opening an account, ${owner}`); 679 | } 680 | 681 | // 3) Public methods 682 | 683 | // Public interface 684 | getMovements() { 685 | return this.#movements; 686 | } 687 | 688 | deposit(val) { 689 | this.#movements.push(val); 690 | return this; 691 | } 692 | 693 | withdraw(val) { 694 | this.deposit(-val); 695 | return this; 696 | } 697 | 698 | requestLoan(val) { 699 | // if (this.#approveLoan(val)) { 700 | if (this._approveLoan(val)) { 701 | this.deposit(val); 702 | console.log(`Loan approved`); 703 | return this; 704 | } 705 | } 706 | 707 | static helper() { 708 | console.log('Helper'); 709 | } 710 | 711 | // 4) Private methods 712 | // #approveLoan(val) { 713 | _approveLoan(val) { 714 | return true; 715 | } 716 | } 717 | 718 | const acc1 = new Account('Jonas', 'EUR', 1111); 719 | 720 | // acc1._movements.push(250); 721 | // acc1._movements.push(-140); 722 | // acc1.approveLoan(1000); 723 | 724 | acc1.deposit(250); 725 | acc1.withdraw(140); 726 | acc1.requestLoan(1000); 727 | console.log(acc1.getMovements()); 728 | console.log(acc1); 729 | Account.helper(); 730 | 731 | // console.log(acc1.#movements); 732 | // console.log(acc1.#pin); 733 | // console.log(acc1.#approveLoan(100)); 734 | ``` 735 | 736 | ## Chaining Methods 737 | ```js 738 | // Chaining 739 | acc1.deposit(300).deposit(500).withdraw(35).requestLoan(25000).withdraw(4000); 740 | console.log(acc1.getMovements()); 741 | ``` 742 | 743 | ## ES6 Classes Summary 744 | ![[13. Object-Oriented Programming (O0P) With JavaScript-1663995283090.jpeg]] 745 | 746 | ## Coding Challenge #4 747 | 1. Re-create challenge #3, but this time using ES6 classes: create an 'EVCl' child class of the 'CarCl' class 748 | 2. Make the 'charge' property private; 749 | 3. Implement the ability to chain the 'accelerate' and 'chargeBattery' methods of this class, and also update the 'brake' method in the 'CarCl' class. They experiment with chining! 750 | 751 | DATA CAR 1: 'Rivian' going at 120 km/h, with a charge of 23% 752 | 753 | ```js 754 | class CarCl { 755 | constructor(make, speed) { 756 | this.make = make; 757 | this.speed = speed; 758 | } 759 | 760 | accelerate() { 761 | this.speed += 10; 762 | console.log(`${this.make} is going at ${this.speed} km/h`); 763 | } 764 | 765 | brake() { 766 | this.speed -= 5; 767 | console.log(`${this.make} is going at ${this.speed} km/h`); 768 | return this; 769 | } 770 | 771 | get speedUS() { 772 | return this.speed / 1.6; 773 | } 774 | 775 | set speedUS(speed) { 776 | this.speed = speed * 1.6; 777 | } 778 | } 779 | 780 | class EVCl extends CarCl { 781 | #charge; 782 | 783 | constructor(make, speed, charge) { 784 | super(make, speed); 785 | this.#charge = charge; 786 | } 787 | 788 | chargeBattery(chargeTo) { 789 | this.#charge = chargeTo; 790 | return this; 791 | } 792 | 793 | accelerate() { 794 | this.speed += 20; 795 | this.#charge--; 796 | console.log( 797 | `${this.make} is going at ${this.speed} km/h, with a charge of ${ 798 | this.#charge 799 | }` 800 | ); 801 | return this; 802 | } 803 | } 804 | 805 | const rivian = new EVCl('Rivian', 120, 23); 806 | console.log(rivian); 807 | // console.log(rivian.#charge); 808 | rivian 809 | .accelerate() 810 | .accelerate() 811 | .accelerate() 812 | .brake() 813 | .chargeBattery(50) 814 | .accelerate(); 815 | 816 | console.log(rivian.speedUS); 817 | ``` -------------------------------------------------------------------------------- /13. Object-Oriented Programming (O0P) With JavaScript.md: -------------------------------------------------------------------------------- 1 | # 13. Object-Oriented Programming (O0P) With JavaScript 2 | --- 3 | ## What is Object Oriented Programming? 4 | ![[13. Object-Oriented Programming (O0P) With JavaScript-1663898980491.jpeg]] 5 | 6 | ### Classes and Instances (Traditional OOP) 7 | ![[13. Object-Oriented Programming (O0P) With JavaScript-1663899008964.jpeg]] 8 | 9 | ### The 4 Fundamental OOP Principle 10 | ![[13. Object-Oriented Programming (O0P) With JavaScript-1663899032649.jpeg]] 11 | 12 | ### Abstraction 13 | - **Abstraction**: Ignoring or hiding details that *don't matter*, allowing us to 14 | get _an overview_ perspective of the thing we're implementing, instead of 15 | messing with details that don't really matter to our implementation. 16 | ![[13. Object-Oriented Programming (O0P) With JavaScript-1663899059332.jpeg]] 17 | 18 | ### Encapsulation Inheritance 19 | - **Encapsulation**: Keeping properties and methods *private* inside the class, 20 | SO they are *not accessible from outside the class*. Some methods can 21 | be exposed as a public interface (API). 22 | ![[13. Object-Oriented Programming (O0P) With JavaScript-1663899150174.jpeg]] 23 | 24 | ### Polymorphism 25 | - **Inheritance**: Making all properties and methods of a certain class *available 26 | to a child class*, forming a hierarchical relationship between classes. This 27 | allows us to *reuse common logic* and to model real-world relationships. 28 | ![[13. Object-Oriented Programming (O0P) With JavaScript-1663899193652.jpeg]] 29 | 30 | ## 00P in JavaScript 31 | - Polymorphism: A child class can *overwrite* a method it inherited from a 32 | parent class [it's more complex that that, but enough for our purposes]. 33 | ![[13. Object-Oriented Programming (O0P) With JavaScript-1663899232684.jpeg]] 34 | 35 | ## 00P in JavaScript 36 | ![[13. Object-Oriented Programming (O0P) With JavaScript-1663899253131.jpeg]] 37 | 38 | **3 Ways of Implementing Prototypical Inheritance** 39 | ![[13. Object-Oriented Programming (O0P) With JavaScript-1663899280934.jpeg]] 40 | 41 | ## Constructor Functions and the new Operator 42 | - The only difference between a regular function, and a function that we call constructor function, is that we call a constructor function with the new operator. 43 | - An arrow function will actually not work as a function constructor. And that's because it doesn't have its own this keyword 44 | - And constructor functions have been used since the beginning of JavaScript to kind of simulate classes. 45 | - Just note that function constructors are not really a feature of the JavaScript language. Instead, they are simply a pattern that has been developed by other developers. And now everyone simply uses this. 46 | ```js 47 | /* all the objects that are created through this constructor function here will inherit, so they will get access to all the methods and properties that are defined on this prototype property. */ 48 | const Person = function (firstName, birthYear) { 49 | // instance properties 50 | this.firstName = firstName; 51 | this.birthYear = birthYear; 52 | 53 | // You should never create a method inside of a constructor fucnction 54 | // this.calcAge = 2047 - this.birthYear; 55 | }; 56 | 57 | new Person('Ahmed', 1997); 58 | 59 | // 1. New {} is created 60 | // 2. function is called, this = {} 61 | // 3. {} is linked to prototype 62 | // 4. fucntion automatically return {} 63 | 64 | const ahmed = new Person('Ahmed', 1997); 65 | const salaria = new Person('Salaria', 1998); 66 | 67 | console.log(ahmed); 68 | console.log(salaria); 69 | 70 | console.log(ahmed instanceof Person); // true 71 | ``` 72 | 73 | ## Prototypes 74 | ```js 75 | // Prototypes 76 | console.log(Person.prototype); 77 | 78 | Person.prototype.calcAge = function () { 79 | console.log(2047 - this.birthYear); 80 | }; 81 | 82 | ahmed.calcAge(); 83 | salaria.calcAge(); 84 | 85 | console.log(ahmed.__proto__); 86 | 87 | // Where does this proto property comes from? From step number 3 88 | // 1. New {} is created 89 | // 2. function is called, this = {} 90 | // 3. {} is linked to prototype 91 | // 4. fucntion automatically return {} 92 | 93 | console.log(ahmed.__proto__ === Person.prototype); 94 | 95 | /* So person dot prototype here is actually not the prototype of person. But instead, it is what's gonna be used as the prototype of all the objects that are created with the person constructor function. 96 | So that's a subtle a but important difference that you need to keep in mind. And, in fact, what I just said that is confirmed by this comparison */ 97 | 98 | console.log(Person.prototype.isPrototypeOf(ahmed)); // true 99 | console.log(Person.prototype.isPrototypeOf(salaria)); // true 100 | console.log(Person.prototype.isPrototypeOf(Person)); // false 101 | 102 | // Rather than calling it prototype is should have been called .prototypeOfLinkedObjects 103 | 104 | Person.prototype.species = 'Homo Sapiens'; 105 | console.log(ahmed.species, salaria.species); 106 | 107 | console.log(ahmed.hasOwnPropery('firstName')); // true 108 | console.log(ahmed.hasOwnPropery('species')); // false 109 | ``` 110 | 111 | ## Prototypal Inheritance and The Prototype Chain 112 | **How Prototypical Inheritance / Delegation works?** 113 | ![[13. Object-Oriented Programming (O0P) With JavaScript-1663906658628.jpeg]] 114 | **The Prototype Chain** 115 | ![[13. Object-Oriented Programming (O0P) With JavaScript-1663906701353.jpeg]] 116 | 117 | ## Prototypal Inheritance on Built-In Objects 118 | ```js 119 | /////////////////////////////////////// 120 | // Prototypal Inheritance on Built-In Objects 121 | console.log(jonas.__proto__); 122 | // Object.prototype (top of prototype chain) 123 | console.log(jonas.__proto__.__proto__); 124 | console.log(jonas.__proto__.__proto__.__proto__); 125 | 126 | console.dir(Person.prototype.constructor); 127 | 128 | const arr = [3, 6, 6, 5, 6, 9, 9]; // new Array === [] 129 | console.log(arr.__proto__); 130 | console.log(arr.__proto__ === Array.prototype); 131 | 132 | console.log(arr.__proto__.__proto__); 133 | 134 | Array.prototype.unique = function () { 135 | return [...new Set(this)]; 136 | }; 137 | 138 | console.log(arr.unique()); 139 | 140 | const h1 = document.querySelector('h1'); 141 | console.dir(x => x + 1); 142 | ``` 143 | 144 | ## Coding Challenge #1 145 | 1. Use a constructor function to implement a Car. A car has a make and a speed property. The speed property is the current speed of the car in km/h; 146 | 2. Implement an 'accelerate' method that will increase the car's speed by 10, and log the new speed to the console; 147 | 3. Implement a 'brake' method that will decrease the car's speed by 5, and log the new speed to the console; 148 | 4. Create 2 car objects and experiment with calling 'accelerate' and 'brake' multiple times on each of them. 149 | 150 | DATA CAR 1: 'BMW' going at 120 km/h 151 | DATA CAR 2: 'Mercedes' going at 95 km/h 152 | 153 | ```js 154 | const Car = function (make, speed) { 155 | this.make = make; 156 | this.speed = speed; 157 | }; 158 | 159 | Car.prototype.accelerate = function () { 160 | this.speed += 10; 161 | console.log(`${this.make} is going at ${this.speed} km/h`); 162 | }; 163 | 164 | Car.prototype.brake = function () { 165 | this.speed -= 5; 166 | console.log(`${this.make} is going at ${this.speed} km/h`); 167 | }; 168 | 169 | const bmw = new Car('BMW', 120); 170 | const mercedes = new Car('Mercedes', 95); 171 | 172 | bmw.accelerate(); 173 | bmw.accelerate(); 174 | bmw.brake(); 175 | bmw.accelerate(); 176 | ``` 177 | 178 | ## ES6 Classes 179 | ```js 180 | /////////////////////////////////////// 181 | // ES6 Classes 182 | 183 | // Class expression 184 | // const PersonCl = class {} 185 | 186 | // Class declaration 187 | class PersonCl { 188 | constructor(fullName, birthYear) { 189 | this.fullName = fullName; 190 | this.birthYear = birthYear; 191 | } 192 | 193 | // Instance methods 194 | // Methods will be added to .prototype property 195 | calcAge() { 196 | console.log(2037 - this.birthYear); 197 | } 198 | 199 | greet() { 200 | console.log(`Hey ${this.fullName}`); 201 | } 202 | 203 | get age() { 204 | return 2037 - this.birthYear; 205 | } 206 | 207 | // Set a property that already exists 208 | set fullName(name) { 209 | if (name.includes(' ')) this._fullName = name; 210 | else alert(`${name} is not a full name!`); 211 | } 212 | 213 | get fullName() { 214 | return this._fullName; 215 | } 216 | 217 | ``` 218 | 219 | ## Setters and Getters 220 | - So every object in JavaScript can have setter and getter properties. And we call these special properties assessor properties, while the more normal properties are called data properties. So getters and setters are basically functions that get and set a value so just as the name says, but on the outside they still look like regular properties. 221 | ```js 222 | /////////////////////////////////////// 223 | // Setters and Getters 224 | const account = { 225 | owner: 'Jonas', 226 | movements: [200, 530, 120, 300], 227 | 228 | get latest() { 229 | return this.movements.slice(-1).pop(); 230 | }, 231 | 232 | set latest(mov) { 233 | this.movements.push(mov); 234 | }, 235 | }; 236 | 237 | console.log(account.latest); 238 | 239 | account.latest = 50; 240 | console.log(account.movements); 241 | ``` 242 | 243 | ## Static Methods 244 | ```js 245 | // Static method 246 | static hey() { 247 | console.log('Hey there 👋'); 248 | console.log(this); 249 | } 250 | } 251 | 252 | const jessica = new PersonCl('Jessica Davis', 1996); 253 | console.log(jessica); 254 | jessica.calcAge(); 255 | console.log(jessica.age); 256 | 257 | console.log(jessica.__proto__ === PersonCl.prototype); 258 | 259 | // PersonCl.prototype.greet = function () { 260 | // console.log(`Hey ${this.firstName}`); 261 | // }; 262 | jessica.greet(); 263 | 264 | // 1. Classes are NOT hoisted 265 | // 2. Classes are first-class citizens 266 | // 3. Classes are executed in strict mode 267 | 268 | const walter = new PersonCl('Walter White', 1965); 269 | // PersonCl.hey(); 270 | ``` 271 | 272 | ## Object.create 273 | ```js 274 | /////////////////////////////////////// 275 | // Object.create 276 | const PersonProto = { 277 | calcAge() { 278 | console.log(2037 - this.birthYear); 279 | }, 280 | 281 | init(firstName, birthYear) { 282 | this.firstName = firstName; 283 | this.birthYear = birthYear; 284 | }, 285 | }; 286 | 287 | const steven = Object.create(PersonProto); 288 | console.log(steven); 289 | steven.name = 'Steven'; 290 | steven.birthYear = 2002; 291 | steven.calcAge(); 292 | 293 | console.log(steven.__proto__ === PersonProto); 294 | 295 | const sarah = Object.create(PersonProto); 296 | sarah.init('Sarah', 1979); 297 | sarah.calcAge(); 298 | ``` 299 | 300 | ## Coding Challenge #2 301 | ```js 302 | class CarCl { 303 | constructor(make, speed) { 304 | this.make = make; 305 | this.speed = speed; 306 | } 307 | 308 | accelerate() { 309 | this.speed += 10; 310 | console.log(`${this.make} is going at ${this.speed} km/h`); 311 | } 312 | 313 | brake() { 314 | this.speed -= 5; 315 | console.log(`${this.make} is going at ${this.speed} km/h`); 316 | } 317 | 318 | get speedUS() { 319 | return this.speed / 1.6; 320 | } 321 | 322 | set speedUS(speed) { 323 | this.speed = speed * 1.6; 324 | } 325 | } 326 | 327 | const ford = new CarCl('Ford', 120); 328 | console.log(ford.speedUS); 329 | ford.accelerate(); 330 | ford.accelerate(); 331 | ford.brake(); 332 | ford.speedUS = 50; 333 | console.log(ford); 334 | ``` 335 | 336 | ## Inheritance Between "Classes": Constructor Functions 337 | ```js 338 | /////////////////////////////////////// 339 | // Inheritance Between "Classes": Constructor Functions 340 | 341 | const Person = function (firstName, birthYear) { 342 | this.firstName = firstName; 343 | this.birthYear = birthYear; 344 | }; 345 | 346 | Person.prototype.calcAge = function () { 347 | console.log(2037 - this.birthYear); 348 | }; 349 | 350 | const Student = function (firstName, birthYear, course) { 351 | Person.call(this, firstName, birthYear); 352 | this.course = course; 353 | }; 354 | 355 | // Linking prototypes 356 | Student.prototype = Object.create(Person.prototype); // At this point, student.prototype is empty 357 | 358 | Student.prototype.introduce = function () { 359 | console.log(`My name is ${this.firstName} and I study ${this.course}`); 360 | }; 361 | 362 | const mike = new Student('Mike', 2020, 'Computer Science'); 363 | mike.introduce(); 364 | mike.calcAge(); 365 | 366 | console.log(mike.__proto__); 367 | console.log(mike.__proto__.__proto__); 368 | 369 | console.log(mike instanceof Student); 370 | console.log(mike instanceof Person); 371 | console.log(mike instanceof Object); 372 | 373 | Student.prototype.constructor = Student; 374 | console.dir(Student.prototype.constructor); 375 | ``` 376 | 377 | ## Coding Challenge #3 378 | 1. Use a constructor function to implement an Electric Car (called EV) as a CHILD "class" of Car. Besides a make and current speed, the EV also has the current battery charge in % ('charge' property); 379 | 2. Implement a 'chargeBattery' method which takes an argument 'chargeTo' and sets the battery charge to 'chargeTo'; 380 | 3. Implement an 'accelerate' method that will increase the car's speed by 20, and decrease the charge by 1%. Then log a message like this: 'Tesla going at 140 km/h, with a charge of 22%'; 381 | 4. Create an electric car object and experiment with calling 'accelerate', 'brake' and 'chargeBattery' (charge to 90%). Notice what happens when you 'accelerate'! HINT: Review the definiton of polymorphism 😉 382 | 383 | DATA CAR 1: 'Tesla' going at 120 km/h, with a charge of 23% 384 | ```js 385 | const Car = function (make, speed) { 386 | this.make = make; 387 | this.speed = speed; 388 | }; 389 | 390 | Car.prototype.accelerate = function () { 391 | this.speed += 10; 392 | console.log(`${this.make} is going at ${this.speed} km/h`); 393 | }; 394 | 395 | Car.prototype.brake = function () { 396 | this.speed -= 5; 397 | console.log(`${this.make} is going at ${this.speed} km/h`); 398 | }; 399 | 400 | const EV = function (make, speed, charge) { 401 | Car.call(this, make, speed); 402 | this.charge = charge; 403 | }; 404 | 405 | // Link the prototypes 406 | EV.prototype = Object.create(Car.prototype); 407 | 408 | EV.prototype.chargeBattery = function (chargeTo) { 409 | this.charge = chargeTo; 410 | }; 411 | 412 | EV.prototype.accelerate = function () { 413 | this.speed += 20; 414 | this.charge--; 415 | console.log( 416 | `${this.make} is going at ${this.speed} km/h, with a charge of ${this.charge}` 417 | ); 418 | }; 419 | 420 | const tesla = new EV('Tesla', 120, 23); 421 | tesla.chargeBattery(90); 422 | console.log(tesla); 423 | tesla.brake(); 424 | tesla.accelerate(); 425 | ``` 426 | 427 | ## Inheritance Between "Classes": ES6 Classes 428 | ![[13. Object-Oriented Programming (O0P) With JavaScript-1663994719743.jpeg]] 429 | 430 | ![[13. Object-Oriented Programming (O0P) With JavaScript-1663994740330.jpeg]] 431 | 432 | ![[13. Object-Oriented Programming (O0P) With JavaScript-1663994769380.jpeg]] 433 | 434 | ![[13. Object-Oriented Programming (O0P) With JavaScript-1663994814589.jpeg]] 435 | 436 | ```js 437 | /////////////////////////////////////// 438 | // Inheritance Between "Classes": ES6 Classes 439 | 440 | class PersonCl { 441 | constructor(fullName, birthYear) { 442 | this.fullName = fullName; 443 | this.birthYear = birthYear; 444 | } 445 | 446 | // Instance methods 447 | calcAge() { 448 | console.log(2037 - this.birthYear); 449 | } 450 | 451 | greet() { 452 | console.log(`Hey ${this.fullName}`); 453 | } 454 | 455 | get age() { 456 | return 2037 - this.birthYear; 457 | } 458 | 459 | set fullName(name) { 460 | if (name.includes(' ')) this._fullName = name; 461 | else alert(`${name} is not a full name!`); 462 | } 463 | 464 | get fullName() { 465 | return this._fullName; 466 | } 467 | 468 | // Static method 469 | static hey() { 470 | console.log('Hey there 👋'); 471 | } 472 | } 473 | 474 | class StudentCl extends PersonCl { 475 | constructor(fullName, birthYear, course) { 476 | // Always needs to happen first! 477 | super(fullName, birthYear); 478 | this.course = course; 479 | } 480 | 481 | introduce() { 482 | console.log(`My name is ${this.fullName} and I study ${this.course}`); 483 | } 484 | 485 | calcAge() { 486 | console.log( 487 | `I'm ${ 488 | 2037 - this.birthYear 489 | } years old, but as a student I feel more like ${ 490 | 2037 - this.birthYear + 10 491 | }` 492 | ); 493 | } 494 | } 495 | 496 | const martha = new StudentCl('Martha Jones', 2012, 'Computer Science'); 497 | martha.introduce(); 498 | martha.calcAge(); 499 | ``` 500 | 501 | ## Inheritance Between "Classes": Object.create 502 | ![[13. Object-Oriented Programming (O0P) With JavaScript-1663994609934.jpeg]] 503 | 504 | ```js 505 | /////////////////////////////////////// 506 | // Inheritance Between "Classes": Object.create 507 | 508 | const PersonProto = { 509 | calcAge() { 510 | console.log(2037 - this.birthYear); 511 | }, 512 | 513 | init(firstName, birthYear) { 514 | this.firstName = firstName; 515 | this.birthYear = birthYear; 516 | }, 517 | }; 518 | 519 | const steven = Object.create(PersonProto); 520 | 521 | const StudentProto = Object.create(PersonProto); 522 | StudentProto.init = function (firstName, birthYear, course) { 523 | PersonProto.init.call(this, firstName, birthYear); 524 | this.course = course; 525 | }; 526 | 527 | StudentProto.introduce = function () { 528 | // BUG in video: 529 | // console.log(`My name is ${this.fullName} and I study ${this.course}`); 530 | 531 | // FIX: 532 | console.log(`My name is ${this.firstName} and I study ${this.course}`); 533 | }; 534 | 535 | const jay = Object.create(StudentProto); 536 | jay.init('Jay', 2010, 'Computer Science'); 537 | jay.introduce(); 538 | jay.calcAge(); 539 | ``` 540 | 541 | ## Another Class Example 542 | ```js 543 | class Account { 544 | constructor(owner, currency, pin) { 545 | this.owner = owner; 546 | this.currency = currency; 547 | this.pin = pin; 548 | this.movements = []; 549 | this.locale = navigator.language; 550 | console.log(`Thanks for opening an account ${this.name}`); 551 | } 552 | 553 | // Public Interface 554 | 555 | deposit(val) { 556 | this.movements.push(val); 557 | } 558 | withdraw(val) { 559 | this.deposit(-val); 560 | } 561 | approveLoan(val) { 562 | return true; 563 | } 564 | requestLoan(val) { 565 | if (this.approveLoan(val)) { 566 | this.deposit(val); 567 | console.log('Loan Approved'); 568 | } 569 | } 570 | } 571 | 572 | const acc1 = new Account('xoraus', 'RUP', 1111); 573 | 574 | acc1.deposit(250); 575 | acc1.deposit(140); 576 | acc1.requestLoan(1000); 577 | // acc1.approveLoan(1000); This method should only be available to the requestLoan method 578 | // Therefore we need data encapsulation and data privacy 579 | 580 | console.log(acc1); 581 | // console.log(acc1.pin); - This should not be accessible 582 | ``` 583 | 584 | ## Encapsulation: Protected Properties and Methods 585 | - Now first, remember that encapsulation basically means to keep some properties and methods private inside the class so that they are not accessible from outside of the class. Then the rest of the methods are basically exposed as a public interface, which we can also call API. 586 | - Now, there are two big reasons why we need encapsulation and data privacy. 587 | - So first it is to prevent code from outside of a class to accidentally manipulate or data inside the class. 588 | - Now, the second reason is that when we expose only a small interface so a small API consisting only of a few public methods then we can change all the other internal methods with more confidence. Because in this case, we can be sure that external code does not rely on these private methods. 589 | - Encapsulation means information hiding. It's about hiding as much as possible of the object's internal parts and exposing a minimal public interface. The simplest and most elegant way to create encapsulation in JavaScript is using closures. A closure can be created as a function with private state. 590 | 591 | ```js 592 | 'use strict'; 593 | 594 | class Account { 595 | constructor(owner, currency, pin) { 596 | this.owner = owner; 597 | this.currency = currency; 598 | 599 | // Protected Property 600 | this._pin = pin; 601 | this._movements = []; 602 | this.locale = navigator.language; 603 | console.log(`Thanks for opening an account ${this.name}`); 604 | } 605 | 606 | // Public Interface 607 | 608 | getMovements() { 609 | return this._movements; 610 | } 611 | 612 | deposit(val) { 613 | this._movements.push(val); 614 | } 615 | withdraw(val) { 616 | this.deposit(-val); 617 | } 618 | _approveLoan(val) { 619 | return true; 620 | } 621 | requestLoan(val) { 622 | if (this._approveLoan(val)) { 623 | this.deposit(val); 624 | console.log('Loan Approved'); 625 | } 626 | } 627 | } 628 | 629 | const acc1 = new Account('xoraus', 'RUP', 1111); 630 | 631 | // acc1._movements.push(250); 632 | // acc1._movements.push(-140); 633 | 634 | acc1.deposit(250); 635 | acc1.deposit(140); 636 | 637 | acc1.requestLoan(1000); 638 | // acc1.approveLoan(1000); This method should only be available to the requestLoan method 639 | // Therefore we need data encapsulation and data privacy 640 | 641 | console.log(acc1.getMovements()); 642 | 643 | console.log(acc1); 644 | // console.log(acc1.pin); - This should not be accessible 645 | 646 | ``` 647 | 648 | ## Encapsulation: Private Class Fields and Methods 649 | 650 | ```js 651 | /////////////////////////////////////// 652 | // Encapsulation: Protected Properties and Methods 653 | // Encapsulation: Private Class Fields and Methods 654 | 655 | // 1) Public fields 656 | // 2) Private fields 657 | // 3) Public methods 658 | // 4) Private methods 659 | // (there is also the static version) 660 | 661 | class Account { 662 | // 1) Public fields (instances) 663 | locale = navigator.language; 664 | 665 | // 2) Private fields (instances) 666 | #movements = []; 667 | #pin; 668 | 669 | constructor(owner, currency, pin) { 670 | this.owner = owner; 671 | this.currency = currency; 672 | this.#pin = pin; 673 | 674 | // Protected property 675 | // this._movements = []; 676 | // this.locale = navigator.language; 677 | 678 | console.log(`Thanks for opening an account, ${owner}`); 679 | } 680 | 681 | // 3) Public methods 682 | 683 | // Public interface 684 | getMovements() { 685 | return this.#movements; 686 | } 687 | 688 | deposit(val) { 689 | this.#movements.push(val); 690 | return this; 691 | } 692 | 693 | withdraw(val) { 694 | this.deposit(-val); 695 | return this; 696 | } 697 | 698 | requestLoan(val) { 699 | // if (this.#approveLoan(val)) { 700 | if (this._approveLoan(val)) { 701 | this.deposit(val); 702 | console.log(`Loan approved`); 703 | return this; 704 | } 705 | } 706 | 707 | static helper() { 708 | console.log('Helper'); 709 | } 710 | 711 | // 4) Private methods 712 | // #approveLoan(val) { 713 | _approveLoan(val) { 714 | return true; 715 | } 716 | } 717 | 718 | const acc1 = new Account('Jonas', 'EUR', 1111); 719 | 720 | // acc1._movements.push(250); 721 | // acc1._movements.push(-140); 722 | // acc1.approveLoan(1000); 723 | 724 | acc1.deposit(250); 725 | acc1.withdraw(140); 726 | acc1.requestLoan(1000); 727 | console.log(acc1.getMovements()); 728 | console.log(acc1); 729 | Account.helper(); 730 | 731 | // console.log(acc1.#movements); 732 | // console.log(acc1.#pin); 733 | // console.log(acc1.#approveLoan(100)); 734 | ``` 735 | 736 | ## Chaining Methods 737 | ```js 738 | // Chaining 739 | acc1.deposit(300).deposit(500).withdraw(35).requestLoan(25000).withdraw(4000); 740 | console.log(acc1.getMovements()); 741 | ``` 742 | 743 | ## ES6 Classes Summary 744 | ![[13. Object-Oriented Programming (O0P) With JavaScript-1663995283090.jpeg]] 745 | 746 | ## Coding Challenge #4 747 | 1. Re-create challenge #3, but this time using ES6 classes: create an 'EVCl' child class of the 'CarCl' class 748 | 2. Make the 'charge' property private; 749 | 3. Implement the ability to chain the 'accelerate' and 'chargeBattery' methods of this class, and also update the 'brake' method in the 'CarCl' class. They experiment with chining! 750 | 751 | DATA CAR 1: 'Rivian' going at 120 km/h, with a charge of 23% 752 | 753 | ```js 754 | class CarCl { 755 | constructor(make, speed) { 756 | this.make = make; 757 | this.speed = speed; 758 | } 759 | 760 | accelerate() { 761 | this.speed += 10; 762 | console.log(`${this.make} is going at ${this.speed} km/h`); 763 | } 764 | 765 | brake() { 766 | this.speed -= 5; 767 | console.log(`${this.make} is going at ${this.speed} km/h`); 768 | return this; 769 | } 770 | 771 | get speedUS() { 772 | return this.speed / 1.6; 773 | } 774 | 775 | set speedUS(speed) { 776 | this.speed = speed * 1.6; 777 | } 778 | } 779 | 780 | class EVCl extends CarCl { 781 | #charge; 782 | 783 | constructor(make, speed, charge) { 784 | super(make, speed); 785 | this.#charge = charge; 786 | } 787 | 788 | chargeBattery(chargeTo) { 789 | this.#charge = chargeTo; 790 | return this; 791 | } 792 | 793 | accelerate() { 794 | this.speed += 20; 795 | this.#charge--; 796 | console.log( 797 | `${this.make} is going at ${this.speed} km/h, with a charge of ${ 798 | this.#charge 799 | }` 800 | ); 801 | return this; 802 | } 803 | } 804 | 805 | const rivian = new EVCl('Rivian', 120, 23); 806 | console.log(rivian); 807 | // console.log(rivian.#charge); 808 | rivian 809 | .accelerate() 810 | .accelerate() 811 | .accelerate() 812 | .brake() 813 | .chargeBattery(50) 814 | .accelerate(); 815 | 816 | console.log(rivian.speedUS); 817 | ``` -------------------------------------------------------------------------------- /06. DOM and Events Fundamentals.md: -------------------------------------------------------------------------------- 1 | # 6. JavaScript in the Browser - DOM and Events Fundamentals 2 | --- 3 | 4 | ## PROJECT ##1: Guess My Number! 5 | 6 | ```js 7 | 'use strict'; 8 | 9 | document.querySelector('.message'); 10 | console.log(document.querySelector('.message').textContent); 11 | ``` 12 | 13 | - (.) notation for classes 14 | - (##) for selecting ids 15 | - .textContent to select the text. 16 | 17 | ## What's the DOM and DOM Manipulation 18 | 19 | **DOM** - Document Object Model: Structured Representation of Html Documents. Allows Javascript to Access Html Elements and Styles to Manipulate Them. Dom Changes text, HTML attributes, and even CSS styles. 20 | 21 | ## The DOM Tree Structure 22 | 23 | ![[6. JavaScript in the Browser - DOM and Events Fundamentals-1661525143222.jpeg]] 24 | 25 | 26 | ## DOM !== Javascript 27 | 28 | ![[6. JavaScript in the Browser - DOM and Events Fundamentals-1661525192615.jpeg]] 29 | 30 | ## Selecting and Manipulating Elements 31 | ```js 32 | console.log(document.querySelector('.message').textContent); 33 | document.querySelector('.message').textContent = '🎉 Correct Number!'; 34 | 35 | document.querySelector('.number').textContent = 13; 36 | document.querySelector('.score').textContent = 10; 37 | 38 | document.querySelector('.guess').value = 23; // we use ".value" to read from the input field. 39 | console.log(document.querySelector('.guess').value); 40 | ``` 41 | 42 | ## Handling Click Events 43 | 44 | - **Event Listener** - Now, an event is something that happens on the page. For example, a mouse click, or a mouse moving, or a key press, 45 | - And if a function is just is value, then we can also pass it into another function as an argument. 46 | 47 | ```js 48 | document.querySelector('.message'); 49 | console.log(document.querySelector('.message').textContent); 50 | 51 | document.querySelector('.check').addEventListener('click', function () { 52 | const guess = console.log(document.querySelector('.guess').value); 53 | console.log(guess, typeof guess); 54 | 55 | if (!guess) { 56 | document.querySelector('.message').textContent = '🚫 No Number!'; 57 | } 58 | }); 59 | 60 | ``` 61 | 62 | - whenever we get the user value it's in string format 63 | - So we selected this button here using querySelector. 64 | - And then we use the addEventListener method on that element to attach an event handler. 65 | - And that event handler is this function here. 66 | 67 | ## Implementing the Game Logic 68 | ```js 69 | const secretNumber = Math.trunc(Math.random() * 20) + 1; 70 | let score = 20; 71 | 72 | document.querySelector('.message'); 73 | console.log(document.querySelector('.message').textContent); 74 | 75 | document.querySelector('.check').addEventListener('click', function () { 76 | const guess = Number(document.querySelector('.guess').value); 77 | console.log(guess, typeof guess); 78 | 79 | // When there is no input 80 | if (!guess) { 81 | document.querySelector('.message').textContent = '🚫 No Number!'; 82 | } else if (guess === secretNumber) { // When player wins 83 | document.querySelector('.message').textContent = '🎉 Correct Number!'; 84 | } else if (guess > secretNumber) { // When guess is too high 85 | if (score > 1) { 86 | document.querySelector('.message').textContent = '📈 Too high!'; 87 | score--; 88 | document.querySelector('.score').textContent = score; 89 | } else { 90 | document.querySelector('.message').textContent = '💥 You lost the game!'; 91 | document.querySelector('.score').textContent = 0; 92 | } 93 | // When guess is too low 94 | } else if (guess < secretNumber) { 95 | if (score > 1) { 96 | document.querySelector('.message').textContent = '📉 Too low!'; 97 | score--; 98 | document.querySelector('.score').textContent = score; 99 | } else { 100 | document.querySelector('.message').textContent = '💥 You lost the game!'; 101 | document.querySelector('.score').textContent = 0; 102 | } 103 | } 104 | }); 105 | ``` 106 | - And so it's always good to keep a variable which actually holds the data in our code and not just rely on the DOM to hold our data. And the variable can also be called a state variable Because this score is part of the so-called application state which is basically all the data that is relevant to the application. 107 | ## Manipulating CSS Styles 108 | ```js 109 | 110 | ``` 111 | 112 | ## Coding Challenge ##1 113 | ```js 114 | 'use strict'; 115 | 116 | let secretNumber = Math.trunc(Math.random() * 20) + 1; 117 | let score = 20; 118 | let highScore = 0; 119 | console.log(secretNumber); 120 | document.querySelector('.message'); 121 | console.log(document.querySelector('.message').textContent); 122 | 123 | document.querySelector('.check').addEventListener('click', function () { 124 | const guess = Number(document.querySelector('.guess').value); 125 | console.log(guess, typeof guess); 126 | 127 | // When there is no input 128 | if (!guess) { 129 | document.querySelector('.message').textContent = '🚫 No Number!'; 130 | } else if (guess === secretNumber) { 131 | // When player wins 132 | document.querySelector('.message').textContent = '🎉 Correct Number!'; 133 | document.querySelector('.number').textContent = secretNumber; 134 | document.querySelector('body').style.backgroundColor = '##60b347'; 135 | document.querySelector('.number').style.width = '30rem'; 136 | if (score > highScore) { 137 | highScore = score; 138 | } 139 | } else if (guess > secretNumber) { 140 | // When guess is too high 141 | if (score > 1) { 142 | document.querySelector('.message').textContent = '📈 Too high!'; 143 | score--; 144 | document.querySelector('.score').textContent = score; 145 | } else { 146 | document.querySelector('.message').textContent = '💥 You lost the game!'; 147 | document.querySelector('.score').textContent = 0; 148 | } 149 | // When guess is too low 150 | } else if (guess < secretNumber) { 151 | if (score > 1) { 152 | document.querySelector('.message').textContent = '📉 Too low!'; 153 | score--; 154 | document.querySelector('.score').textContent = score; 155 | } else { 156 | document.querySelector('.message').textContent = '💥 You lost the game!'; 157 | document.querySelector('.score').textContent = 0; 158 | } 159 | } 160 | }); 161 | 162 | document.querySelector('.again').addEventListener('click', function () { 163 | document.querySelector('.message').textContent = 'Start Guessing'; 164 | document.querySelector('body').style.backgroundColor = '##222'; 165 | document.querySelector('.number').style.width = '15rem'; 166 | secretNumber = Math.trunc(Math.random() * 20) + 1; 167 | score = 20; 168 | document.querySelector('.score').textContent = score; 169 | document.querySelector('.number').textContent = '?'; 170 | document.querySelector('.guess').value = ''; 171 | document.querySelector('.highscore').textContent = highScore; 172 | }); 173 | 174 | ``` 175 | 176 | ## Implementing High scores 177 | 178 | ```js 179 | if (score > highScore) { 180 | highScore = score; 181 | } 182 | 183 | document.querySelector('.highscore').textContent = highScore; 184 | ``` 185 | 186 | ## Refactoring Our Code: The DRY Principle 187 | ```js 188 | 'use strict'; 189 | let secretNumber = Math.trunc(Math.random() * 20) + 1; 190 | let score = 20; 191 | let highscore = 0; 192 | 193 | const displayMessage = function (message) { 194 | document.querySelector('.message').textContent = message; 195 | }; 196 | 197 | document.querySelector('.check').addEventListener('click', function () { 198 | const guess = Number(document.querySelector('.guess').value); 199 | console.log(guess, typeof guess); 200 | 201 | // When there is no input 202 | if (!guess) { 203 | // document.querySelector('.message').textContent = '⛔️ No number!'; 204 | displayMessage('⛔️ No number!'); 205 | 206 | // When player wins 207 | } else if (guess === secretNumber) { 208 | // document.querySelector('.message').textContent = '🎉 Correct Number!'; 209 | displayMessage('🎉 Correct Number!'); 210 | document.querySelector('.number').textContent = secretNumber; 211 | 212 | document.querySelector('body').style.backgroundColor = '##60b347'; 213 | document.querySelector('.number').style.width = '30rem'; 214 | 215 | if (score > highscore) { 216 | highscore = score; 217 | document.querySelector('.highscore').textContent = highscore; 218 | } 219 | 220 | // When guess is wrong 221 | } else if (guess !== secretNumber) { 222 | if (score > 1) { 223 | // document.querySelector('.message').textContent = 224 | // guess > secretNumber ? '📈 Too high!' : '📉 Too low!'; 225 | displayMessage(guess > secretNumber ? '📈 Too high!' : '📉 Too low!'); 226 | score--; 227 | document.querySelector('.score').textContent = score; 228 | } else { 229 | // document.querySelector('.message').textContent = '💥 You lost the game!'; 230 | displayMessage('💥 You lost the game!'); 231 | document.querySelector('.score').textContent = 0; 232 | } 233 | } 234 | }); 235 | 236 | document.querySelector('.again').addEventListener('click', function () { 237 | score = 20; 238 | secretNumber = Math.trunc(Math.random() * 20) + 1; 239 | 240 | // document.querySelector('.message').textContent = 'Start guessing...'; 241 | displayMessage('Start guessing...'); 242 | document.querySelector('.score').textContent = score; 243 | document.querySelector('.number').textContent = '?'; 244 | document.querySelector('.guess').value = ''; 245 | 246 | document.querySelector('body').style.backgroundColor = '##222'; 247 | document.querySelector('.number').style.width = '15rem'; 248 | }); 249 | ``` 250 | 251 | #### Guess the Number - HTML 252 | ```html 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | Guess My Number! 261 | 262 | 263 |
264 |

Guess My Number!

265 |

(Between 1 and 20)

266 | 267 |
?
268 |
269 |
270 |
271 | 272 | 273 |
274 |
275 |

Start guessing...

276 |

💯 Score: 20

277 |

278 | 🥇 Highscore: 0 279 |

280 |
281 |
282 | 283 | 284 | 285 | 286 | ``` 287 | #### Guess the Number - CSS 288 | ```css 289 | @import url('https://fonts.googleapis.com/css?family=Press+Start+2P&display=swap'); 290 | 291 | * { 292 | margin: 0; 293 | padding: 0; 294 | box-sizing: inherit; 295 | } 296 | 297 | html { 298 | font-size: 62.5%; 299 | box-sizing: border-box; 300 | } 301 | 302 | body { 303 | font-family: 'Press Start 2P', sans-serif; 304 | color: ##eee; 305 | background-color: ##222; 306 | /* background-color: ##60b347; */ 307 | } 308 | 309 | /* LAYOUT */ 310 | header { 311 | position: relative; 312 | height: 35vh; 313 | border-bottom: 7px solid ##eee; 314 | } 315 | 316 | main { 317 | height: 65vh; 318 | color: ##eee; 319 | display: flex; 320 | align-items: center; 321 | justify-content: space-around; 322 | } 323 | 324 | .left { 325 | width: 52rem; 326 | display: flex; 327 | flex-direction: column; 328 | align-items: center; 329 | } 330 | 331 | .right { 332 | width: 52rem; 333 | font-size: 2rem; 334 | } 335 | 336 | /* ELEMENTS STYLE */ 337 | h1 { 338 | font-size: 4rem; 339 | text-align: center; 340 | position: absolute; 341 | width: 100%; 342 | top: 52%; 343 | left: 50%; 344 | transform: translate(-50%, -50%); 345 | } 346 | 347 | .number { 348 | background: ##eee; 349 | color: ##333; 350 | font-size: 6rem; 351 | width: 15rem; 352 | padding: 3rem 0rem; 353 | text-align: center; 354 | position: absolute; 355 | bottom: 0; 356 | left: 50%; 357 | transform: translate(-50%, 50%); 358 | } 359 | 360 | .between { 361 | font-size: 1.4rem; 362 | position: absolute; 363 | top: 2rem; 364 | right: 2rem; 365 | } 366 | 367 | .again { 368 | position: absolute; 369 | top: 2rem; 370 | left: 2rem; 371 | } 372 | 373 | .guess { 374 | background: none; 375 | border: 4px solid ##eee; 376 | font-family: inherit; 377 | color: inherit; 378 | font-size: 5rem; 379 | padding: 2.5rem; 380 | width: 25rem; 381 | text-align: center; 382 | display: block; 383 | margin-bottom: 3rem; 384 | } 385 | 386 | .btn { 387 | border: none; 388 | background-color: ##eee; 389 | color: ##222; 390 | font-size: 2rem; 391 | font-family: inherit; 392 | padding: 2rem 3rem; 393 | cursor: pointer; 394 | } 395 | 396 | .btn:hover { 397 | background-color: ##ccc; 398 | } 399 | 400 | .message { 401 | margin-bottom: 8rem; 402 | height: 3rem; 403 | } 404 | 405 | .label-score { 406 | margin-bottom: 2rem; 407 | } 408 | ``` 409 | 410 | ## PROJECT ##2: Modal Window 411 | ```js 412 | 'use strict'; 413 | 414 | const modal = document.querySelector('.modal'); 415 | const overlay = document.querySelector('.overlay'); 416 | const btnCloseModal = document.querySelector('.close-modal'); 417 | const btnsOpenModal = document.querySelectorAll('.show-modal'); 418 | ``` 419 | 420 | ## Working With Classes 421 | ```js 422 | const openmodal = function () { 423 | console.log('Button clicked'); 424 | modal.classList.remove('hidden'); 425 | overlay.classList.remove('hidden'); 426 | }; 427 | 428 | for (let i = 0; i < btnsOpenModal.length; i++) 429 | btnsOpenModal[i].addEventListener('click', openmodal); 430 | 431 | const CloseModal = function () { 432 | modal.classList.add('hidden'); 433 | overlay.classList.add('hidden'); 434 | }; 435 | 436 | btnCloseModal.addEventListener('click', CloseModal); 437 | 438 | overlay.addEventListener('click', CloseModal); 439 | ``` 440 | - in order to change the appearance of elements on our page. 441 | - classes allow us to basically aggregate multiple CSS properties in just one, like a container.s o each class functions a bit like a container with a lot of properties in it. 442 | - And then here, by adding and removing them, we basically can activate and deactivate certain styles, all at the same time. 443 | 444 | ## Handling an "Esc" Keypress Event 445 | Now, keyboard events are so-called global events because they do not happen on one specific element. And for global events like keyboard events we usually list and on the whole document. 446 | ```js 447 | document.addEventListener('keydown', function (e) { 448 | // console.log(e); 449 | if (e.key === 'Escape' && !modal.classList.contains('hideen')) { 450 | CloseModal(); 451 | } 452 | }); 453 | ``` 454 | ## PROJECT ##3: Pig Game 455 | ```js 456 | 'use strict'; 457 | 458 | // Selecting elements 459 | const score0El = document.querySelector('##score--0'); 460 | const score1El = document.getElementById('score--0'); 461 | const diceEl = document.querySelector('.dice'); 462 | 463 | score0El.textContent = 0; 464 | score1El.textContent = 0; 465 | diceEl.classList.add('hidden'); 466 | 467 | ``` 468 | 469 | ## Rolling the Dice 470 | ```js 471 | 'use strict'; 472 | 473 | // Selecting elements 474 | const score0El = document.querySelector('##score--0'); 475 | const score1El = document.getElementById('score--1'); 476 | 477 | const current0El = document.getElementById('current--0'); 478 | const current1El = document.getElementById('current--1'); 479 | 480 | const diceEl = document.querySelector('.dice'); 481 | const btnNew = document.querySelector('.btn--new'); 482 | const btnRoll = document.querySelector('.btn--roll'); 483 | const btnHold = document.querySelector('.btn--hold'); 484 | 485 | // Starting Conditions 486 | score0El.textContent = 0; 487 | score1El.textContent = 0; 488 | diceEl.classList.add('hidden'); 489 | 490 | const scores = [0, 0]; 491 | let currScore = 0; 492 | let activePlayer = 0; // 0 - Player 1 & 1 -Player 1 493 | 494 | btnRoll.addEventListener('click', function () { 495 | //1. Generate a random dice roll 496 | const dice = Math.trunc(Math.random() * 6) + 1; 497 | console.log(dice); 498 | //2. Diplay dice 499 | diceEl.classList.remove('hidden'); 500 | diceEl.src = `dice-${dice}.png`; 501 | console.log(diceEl.src); 502 | 503 | //3.check for rolled 1 504 | if (dice !== 1) { 505 | currScore += dice; 506 | document.getElementById(`current--${activePlayer}`).textContent = currScore; 507 | } else { 508 | // switch to next player 509 | } 510 | }); 511 | 512 | ``` 513 | 514 | ## Switching the Active Player 515 | ```js 516 | const player0El = document.querySelector('.player--0'); 517 | const player1El = document.querySelector('.player--1'); 518 | 519 | btnRoll.addEventListener('click', function () { 520 | //1. Generate a random dice roll 521 | const dice = Math.trunc(Math.random() * 6) + 1; 522 | //2. Diplay dice 523 | diceEl.classList.remove('hidden'); 524 | diceEl.src = `dice-${dice}.png`; 525 | 526 | //3.check for rolled 1 527 | if (dice !== 1) { 528 | currScore += dice; 529 | document.getElementById(`current--${activePlayer}`).textContent = currScore; 530 | } else { 531 | // switch to next player 532 | document.getElementById(`current--${activePlayer}`).textContent = 0; 533 | currScore = 0; 534 | activePlayer = activePlayer === 0 ? 1 : 0; 535 | player0El.classList.toggle('player--active'); // Toggle will act as on off switch 536 | player1El.classList.toggle('player--active'); 537 | } 538 | }); 539 | ``` 540 | 541 | ## Holding Current Score 542 | ```js 543 | 'use strict'; 544 | 545 | // Selecting elements 546 | 547 | const player0El = document.querySelector('.player--0'); 548 | const player1El = document.querySelector('.player--1'); 549 | 550 | const score0El = document.querySelector('##score--0'); 551 | const score1El = document.getElementById('score--1'); 552 | 553 | const current0El = document.getElementById('current--0'); 554 | const current1El = document.getElementById('current--1'); 555 | 556 | const diceEl = document.querySelector('.dice'); 557 | const btnNew = document.querySelector('.btn--new'); 558 | const btnRoll = document.querySelector('.btn--roll'); 559 | const btnHold = document.querySelector('.btn--hold'); 560 | 561 | // Starting Conditions 562 | score0El.textContent = 0; 563 | score1El.textContent = 0; 564 | diceEl.classList.add('hidden'); 565 | 566 | let scores = [0, 0]; 567 | let currScore = 0; 568 | let activePlayer = 0; // 0 - Player 1 & 1 -Player 1 569 | let playing = true; 570 | 571 | const switchPlayer = function () { 572 | document.getElementById(`current--${activePlayer}`).textContent = 0; 573 | currScore = 0; 574 | activePlayer = activePlayer === 0 ? 1 : 0; 575 | player0El.classList.toggle('player--active'); 576 | player1El.classList.toggle('player--active'); 577 | }; 578 | 579 | btnRoll.addEventListener('click', function () { 580 | if (playing) { 581 | //1. Generate a random dice roll 582 | const dice = Math.trunc(Math.random() * 6) + 1; 583 | //2. Diplay dice 584 | diceEl.classList.remove('hidden'); 585 | diceEl.src = `dice-${dice}.png`; 586 | 587 | //3.check for rolled 1 588 | if (dice !== 1) { 589 | currScore += dice; 590 | document.getElementById(`current--${activePlayer}`).textContent = 591 | currScore; 592 | } else { 593 | // switch to next player 594 | switchPlayer(); 595 | } 596 | } 597 | }); 598 | 599 | btnHold.addEventListener('click', function () { 600 | if (playing) { 601 | scores[activePlayer] += currScore; 602 | document.getElementById(`score--${activePlayer}`).textContent = 603 | scores[activePlayer]; 604 | // Switching Player 605 | if (scores[activePlayer] >= 10) { 606 | playing = false; 607 | diceEl.classList.add('hidden'); 608 | document 609 | .querySelector(`.player--${activePlayer}`) 610 | .classList.add('player--winner'); 611 | document 612 | .querySelector(`.player--${activePlayer}`) 613 | .classList.add('player--active'); 614 | } else { 615 | switchPlayer(); 616 | } 617 | } 618 | }); 619 | 620 | ``` 621 | 622 | ## Resetting the Game 623 | ```js 624 | const init = function () { 625 | scores = [0, 0]; 626 | currentScore = 0; 627 | activePlayer = 0; 628 | playing = true; 629 | 630 | score0El.textContent = 0; 631 | score1El.textContent = 0; 632 | current0El.textContent = 0; 633 | current1El.textContent = 0; 634 | 635 | diceEl.classList.add('hidden'); 636 | player0El.classList.remove('player--winner'); 637 | player1El.classList.remove('player--winner'); 638 | player0El.classList.add('player--active'); 639 | player1El.classList.remove('player--active'); 640 | }; 641 | 642 | btnNew.addEventListener('click', init); 643 | ``` 644 | 645 | ## Complete Code 646 | 647 | #### HTML 648 | ```js 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | Pig Game 657 | 658 | 659 |
660 |
661 |

Player 1

662 |

43

663 |
664 |

Current

665 |

0

666 |
667 |
668 |
669 |

Player 2

670 |

24

671 |
672 |

Current

673 |

0

674 |
675 |
676 | 677 | Playing dice 678 | 679 | 680 | 681 |
682 | 683 | 684 | 685 | 686 | ``` 687 | 688 | #### CSS 689 | ```JS 690 | @import url('https://fonts.googleapis.com/css2?family=Nunito&display=swap'); 691 | 692 | * { 693 | margin: 0; 694 | padding: 0; 695 | box-sizing: inherit; 696 | } 697 | 698 | html { 699 | font-size: 62.5%; 700 | box-sizing: border-box; 701 | } 702 | 703 | body { 704 | font-family: 'Nunito', sans-serif; 705 | font-weight: 400; 706 | height: 100vh; 707 | color: ##333; 708 | background-image: linear-gradient(to top left, ##753682 0%, ##bf2e34 100%); 709 | display: flex; 710 | align-items: center; 711 | justify-content: center; 712 | } 713 | 714 | /* LAYOUT */ 715 | main { 716 | position: relative; 717 | width: 100rem; 718 | height: 60rem; 719 | background-color: rgba(255, 255, 255, 0.35); 720 | backdrop-filter: blur(200px); 721 | filter: blur(); 722 | box-shadow: 0 3rem 5rem rgba(0, 0, 0, 0.25); 723 | border-radius: 9px; 724 | overflow: hidden; 725 | display: flex; 726 | } 727 | 728 | .player { 729 | flex: 50%; 730 | padding: 9rem; 731 | display: flex; 732 | flex-direction: column; 733 | align-items: center; 734 | transition: all 0.75s; 735 | } 736 | 737 | /* ELEMENTS */ 738 | .name { 739 | position: relative; 740 | font-size: 4rem; 741 | text-transform: uppercase; 742 | letter-spacing: 1px; 743 | word-spacing: 2px; 744 | font-weight: 300; 745 | margin-bottom: 1rem; 746 | } 747 | 748 | .score { 749 | font-size: 8rem; 750 | font-weight: 300; 751 | color: ##c7365f; 752 | margin-bottom: auto; 753 | } 754 | 755 | .player--active { 756 | background-color: rgba(255, 255, 255, 0.4); 757 | } 758 | .player--active .name { 759 | font-weight: 700; 760 | } 761 | .player--active .score { 762 | font-weight: 400; 763 | } 764 | 765 | .player--active .current { 766 | opacity: 1; 767 | } 768 | 769 | .current { 770 | background-color: ##c7365f; 771 | opacity: 0.8; 772 | border-radius: 9px; 773 | color: ##fff; 774 | width: 65%; 775 | padding: 2rem; 776 | text-align: center; 777 | transition: all 0.75s; 778 | } 779 | 780 | .current-label { 781 | text-transform: uppercase; 782 | margin-bottom: 1rem; 783 | font-size: 1.7rem; 784 | color: ##ddd; 785 | } 786 | 787 | .current-score { 788 | font-size: 3.5rem; 789 | } 790 | 791 | /* ABSOLUTE POSITIONED ELEMENTS */ 792 | .btn { 793 | position: absolute; 794 | left: 50%; 795 | transform: translateX(-50%); 796 | color: ##444; 797 | background: none; 798 | border: none; 799 | font-family: inherit; 800 | font-size: 1.8rem; 801 | text-transform: uppercase; 802 | cursor: pointer; 803 | font-weight: 400; 804 | transition: all 0.2s; 805 | 806 | background-color: white; 807 | background-color: rgba(255, 255, 255, 0.6); 808 | backdrop-filter: blur(10px); 809 | 810 | padding: 0.7rem 2.5rem; 811 | border-radius: 50rem; 812 | box-shadow: 0 1.75rem 3.5rem rgba(0, 0, 0, 0.1); 813 | } 814 | 815 | .btn::first-letter { 816 | font-size: 2.4rem; 817 | display: inline-block; 818 | margin-right: 0.7rem; 819 | } 820 | 821 | .btn--new { 822 | top: 4rem; 823 | } 824 | .btn--roll { 825 | top: 39.3rem; 826 | } 827 | .btn--hold { 828 | top: 46.1rem; 829 | } 830 | 831 | .btn:active { 832 | transform: translate(-50%, 3px); 833 | box-shadow: 0 1rem 2rem rgba(0, 0, 0, 0.15); 834 | } 835 | 836 | .btn:focus { 837 | outline: none; 838 | } 839 | 840 | .dice { 841 | position: absolute; 842 | left: 50%; 843 | top: 16.5rem; 844 | transform: translateX(-50%); 845 | height: 10rem; 846 | box-shadow: 0 2rem 5rem rgba(0, 0, 0, 0.2); 847 | } 848 | 849 | .player--winner { 850 | background-color: ##2f2f2f; 851 | } 852 | 853 | .player--winner .name { 854 | font-weight: 700; 855 | color: ##c7365f; 856 | } 857 | 858 | .hidden { 859 | display: none; 860 | } 861 | 862 | ``` 863 | 864 | #### Javascript 865 | ```js 866 | 'use strict'; 867 | 868 | // Selecting elements 869 | const player0El = document.querySelector('.player--0'); 870 | const player1El = document.querySelector('.player--1'); 871 | const score0El = document.querySelector('##score--0'); 872 | const score1El = document.getElementById('score--1'); 873 | const current0El = document.getElementById('current--0'); 874 | const current1El = document.getElementById('current--1'); 875 | 876 | const diceEl = document.querySelector('.dice'); 877 | const btnNew = document.querySelector('.btn--new'); 878 | const btnRoll = document.querySelector('.btn--roll'); 879 | const btnHold = document.querySelector('.btn--hold'); 880 | 881 | let scores, currentScore, activePlayer, playing; 882 | 883 | // Starting conditions 884 | const init = function () { 885 | scores = [0, 0]; 886 | currentScore = 0; 887 | activePlayer = 0; 888 | playing = true; 889 | 890 | score0El.textContent = 0; 891 | score1El.textContent = 0; 892 | current0El.textContent = 0; 893 | current1El.textContent = 0; 894 | 895 | diceEl.classList.add('hidden'); 896 | player0El.classList.remove('player--winner'); 897 | player1El.classList.remove('player--winner'); 898 | player0El.classList.add('player--active'); 899 | player1El.classList.remove('player--active'); 900 | }; 901 | init(); 902 | 903 | const switchPlayer = function () { 904 | document.getElementById(`current--${activePlayer}`).textContent = 0; 905 | currentScore = 0; 906 | activePlayer = activePlayer === 0 ? 1 : 0; 907 | player0El.classList.toggle('player--active'); 908 | player1El.classList.toggle('player--active'); 909 | }; 910 | 911 | // Rolling dice functionality 912 | btnRoll.addEventListener('click', function () { 913 | if (playing) { 914 | // 1. Generating a random dice roll 915 | const dice = Math.trunc(Math.random() * 6) + 1; 916 | 917 | // 2. Display dice 918 | diceEl.classList.remove('hidden'); 919 | diceEl.src = `dice-${dice}.png`; 920 | 921 | // 3. Check for rolled 1 922 | if (dice !== 1) { 923 | // Add dice to current score 924 | currentScore += dice; 925 | document.getElementById(`current--${activePlayer}`).textContent = 926 | currentScore; 927 | } else { 928 | // Switch to next player 929 | switchPlayer(); 930 | } 931 | } 932 | }); 933 | 934 | btnHold.addEventListener('click', function () { 935 | if (playing) { 936 | // 1. Add current score to active player's score 937 | scores[activePlayer] += currentScore; 938 | // scores[1] = scores[1] + currentScore 939 | 940 | document.getElementById(`score--${activePlayer}`).textContent = 941 | scores[activePlayer]; 942 | 943 | // 2. Check if player's score is >= 100 944 | if (scores[activePlayer] >= 100) { 945 | // Finish the game 946 | playing = false; 947 | diceEl.classList.add('hidden'); 948 | 949 | document 950 | .querySelector(`.player--${activePlayer}`) 951 | .classList.add('player--winner'); 952 | document 953 | .querySelector(`.player--${activePlayer}`) 954 | .classList.remove('player--active'); 955 | } else { 956 | // Switch to the next player 957 | switchPlayer(); 958 | } 959 | } 960 | }); 961 | 962 | btnNew.addEventListener('click', init); 963 | ``` --------------------------------------------------------------------------------