|
| 1 | +/* #08 CLASES */ |
| 2 | +//bibliography reference |
| 3 | +//Secrets of the JavaScript Ninja (John Resig, Bear Bibeault, Josip Maras) (z-lib.org) |
| 4 | +//Professional JavaScript for web developers by Matt Frisbie [Frisbie, Matt] (z-lib.org) |
| 5 | +//JavaScript Notes for Professionals (GoalKicker.com) (Z-Library) |
| 6 | +//Python para todos (Raúl Gonzáles Duque) |
| 7 | +//GPT |
| 8 | + |
| 9 | +/* **Classes and Objects** |
| 10 | +
|
| 11 | +To understand this paradigm, we first need to comprehend what a class is and what an object is. An object is an entity that groups related state and functionality. The state of the object is defined through variables called attributes, while the functionality is modeled through functions known as the object's methods. |
| 12 | +
|
| 13 | +An example of an object could be a car, which would have attributes such as the brand, the number of doors, or the type of fuel, and methods such as start and stop. Alternatively, it could be any other combination of attributes and methods relevant to our program. |
| 14 | +
|
| 15 | +A class, on the other hand, is nothing more than a generic template from which to instantiate objects; a template that defines what attributes and methods the objects of that class will have. */ |
| 16 | + |
| 17 | + |
| 18 | + |
| 19 | +//Short for console.log() |
| 20 | +let log = console.log.bind(console); |
| 21 | + |
| 22 | +window.addEventListener('load', ()=>{ |
| 23 | + const body = document.querySelector('body'); |
| 24 | + const title = document.createElement('h1'); |
| 25 | + |
| 26 | + body.style.setProperty('background', '#000'); |
| 27 | + body.style.setProperty('text-align', 'center'); |
| 28 | + |
| 29 | + title.textContent = 'Retosparaprogramadores #8.'; |
| 30 | + title.style.setProperty('font-size', '3.5vmax'); |
| 31 | + title.style.setProperty('color', '#fff'); |
| 32 | + title.style.setProperty('line-height', '100vh'); |
| 33 | + |
| 34 | + body.appendChild(title); |
| 35 | + |
| 36 | + setTimeout(()=>{ |
| 37 | + alert('Retosparaprogramadores #8. Please open the Browser Developer Tools.'); |
| 38 | + }, 2000); |
| 39 | + log( 'Retosparaprogramadores #8'); |
| 40 | +}); |
| 41 | + |
| 42 | +/* A class can be composed of the class’s constructor method, instance methods, getters, setters, and |
| 43 | +static class methods. None of these are explicitly required; an empty class definition is valid syntax. |
| 44 | +By default, everything inside a class definition executes in strict mode. */ |
| 45 | + |
| 46 | +/* The fundamental part of most classes is its constructor, which sets up each instance's initial state and handles any |
| 47 | +parameters that were passed when calling new. */ |
| 48 | + |
| 49 | +/* Similar to the function type, there are two primary ways of defining a class: class declarations and |
| 50 | +class expressions. Both use the class keyword and curly braces: |
| 51 | +// class declaration |
| 52 | +class Person {} |
| 53 | +// class expression |
| 54 | +const Animal = class {}; */ |
| 55 | + |
| 56 | +/* Note: even though ES6 has introduced the class keyword, under the |
| 57 | +hood we’re still dealing with good old prototypes; classes are syntactic sugar designed |
| 58 | +to make our lives a bit easier when mimicking classes in JavaScript. */ |
| 59 | + |
| 60 | + |
| 61 | +class User { |
| 62 | + constructor(name, nickname, email) { |
| 63 | + this.name = name; |
| 64 | + this.nickname = nickname; |
| 65 | + this.email = email; |
| 66 | + } |
| 67 | + |
| 68 | + greeting() { |
| 69 | + log(`Hi ${this.nickname}. Welcome to this Roadmap for Developers!`) |
| 70 | + } |
| 71 | + |
| 72 | + getEmail() { |
| 73 | + if(this.email != undefined) { |
| 74 | + return this.email; |
| 75 | + } else { |
| 76 | + log('no email set yet!'); |
| 77 | + return null; |
| 78 | + } |
| 79 | + } |
| 80 | + |
| 81 | + getName() { |
| 82 | + if(this.name != undefined) { |
| 83 | + return this.name; |
| 84 | + } else { |
| 85 | + log('no name set yet!'); |
| 86 | + return null; |
| 87 | + } |
| 88 | + } |
| 89 | + |
| 90 | + getNickname() { |
| 91 | + if(this.nickname != undefined) { |
| 92 | + return this.nickname; |
| 93 | + } else { |
| 94 | + log('no nickname set yet!') |
| 95 | + return null; |
| 96 | + } |
| 97 | + } |
| 98 | + |
| 99 | + setName(name) { |
| 100 | + if(name) this.name = name; |
| 101 | + } |
| 102 | + |
| 103 | + setEmail(email) { |
| 104 | + if(email) this.email = email; |
| 105 | + } |
| 106 | + |
| 107 | + setNickname(nickname) { |
| 108 | + if(nickname) this.nickname = nickname; |
| 109 | + } |
| 110 | + |
| 111 | + userInfo() { |
| 112 | + log(`User name: ${this.name || 'not set'}, User nickname: ${this.nickname || 'not set'}, User email: ${this.email || 'not set'}`); |
| 113 | + } |
| 114 | +} |
| 115 | + |
| 116 | +let user1 = new User('Niko Zen', 'duendeintemporal', '[email protected]'); |
| 117 | +user1.greeting(); // Hi duendeintemporal. Wellcome to this Roadmap for Developers! |
| 118 | + |
| 119 | +user1.userInfo(); // user name: Niko Zen, user nickname: duendeintemporal, user email: [email protected] |
| 120 | + |
| 121 | +user1.setNickname('psicotrogato'); |
| 122 | + |
| 123 | +log(user1.getNickname()); // psicotrogato |
| 124 | + |
| 125 | +/* There is no formal class type in the ECMAScript specification, and in many ways ECMAScript classes |
| 126 | +behave like special functions. Once declared, the class identifier identifies as a function when checked with the typeof operator: */ |
| 127 | + |
| 128 | +log(typeof User); // function |
| 129 | + |
| 130 | +/* As with function constructors, you can use the instanceof operator to test if the constructor prototype appears in the prototype chain of an instance: */ |
| 131 | + |
| 132 | +log(user1 instanceof User); // true |
| 133 | + |
| 134 | +/* Class Inheritance |
| 135 | +Inheritance works just like it does in other object-oriented languages: methods defined on the superclass are accessible in the extending subclass. */ |
| 136 | + |
| 137 | +class Log { |
| 138 | + constructor() { |
| 139 | + this.logger = console.log; |
| 140 | + this.errorLog = console.error; |
| 141 | + } |
| 142 | + |
| 143 | + log(msj) { |
| 144 | + (msj)? this.logger(msj) : this.errorLog('you should provide a msj'); |
| 145 | + } |
| 146 | +} |
| 147 | +// When you extend a class in JavaScript, you must call super() in the constructor of the subclass before you can use this. |
| 148 | + |
| 149 | +class Greeting extends Log { |
| 150 | + constructor(msj) { |
| 151 | + super(); |
| 152 | + this.msj = msj; |
| 153 | + } |
| 154 | +} |
| 155 | + |
| 156 | +let sayHiPeople = new Greeting('Hi everybody. Welcome to the most wierd and lonly place in the cyberspace...'); |
| 157 | + |
| 158 | +sayHiPeople.log(); // Hi everybody. Welcome to the most wierd and lonly place in the cyberspace... |
| 159 | + |
| 160 | +/* Static Methods |
| 161 | +Static methods and properties are defined on the class/constructor itself, not on instance objects. These are specified in a class definition by using the static keyword */ |
| 162 | + |
| 163 | +class ElectroCat { |
| 164 | + static catSay() { |
| 165 | + return 'Miauu'; |
| 166 | + } |
| 167 | + |
| 168 | + static get catThink() { |
| 169 | + return "Let's see if there's some lovely gircat over there"; |
| 170 | + } |
| 171 | +} |
| 172 | + |
| 173 | +log(ElectroCat.catSay()); // Miauu |
| 174 | +log(ElectroCat.catThink); // Let's see if there's some lovely gircat over there |
| 175 | + |
| 176 | +// We can see that static properties are not defined on object instances: |
| 177 | + |
| 178 | +const mishu = new ElectroCat(); |
| 179 | +//log(mishu.catSay()); // undefined OR throw an error |
| 180 | +//log(mishu.catThink); // undefined or throw an error |
| 181 | + |
| 182 | +// However, they are defined on subclasses: |
| 183 | + |
| 184 | +class PoetCat extends ElectroCat {} |
| 185 | +log(PoetCat.catSay()); // Miauu |
| 186 | +log(PoetCat.catThink); // Let's see if there's some lovely gircat over there |
| 187 | + |
| 188 | +/* Getters and setters allow you to define custom behaviour for reading and writing a given property on your class. To the user, they appear the same as any typical property. However, internally a custom function you provide is used to determine the value when the property is accessed (the getter), and to perform any necessary changes when the property is assigned (the setter). |
| 189 | +In a class definition, a getter is written like a no-argument method prefixed by the get keyword. A setter is similar, except that it accepts one argument (the new value being assigned) and the set keyword is used instead. */ |
| 190 | + |
| 191 | +const h_method = Symbol('Hidden method'); |
| 192 | + |
| 193 | +//We use private properties in JavaScript classes to avoid infinite recursion in getters and setters by referencing the private property instead of the public property. |
| 194 | +class GopiElectronica { |
| 195 | + constructor(name) { |
| 196 | + this._name = name; |
| 197 | + } |
| 198 | + |
| 199 | + set name(name) { |
| 200 | + this._name = name; |
| 201 | + } |
| 202 | + |
| 203 | + get name() { |
| 204 | + return this._name; |
| 205 | + } |
| 206 | + |
| 207 | + // You can also create dynamic methods or use symbols or JavaScript expressions as keys |
| 208 | + [h_method]() { |
| 209 | + return 'I will hack you boy'; |
| 210 | + } |
| 211 | +} |
| 212 | + |
| 213 | +const Nicky = new GopiElectronica('Nicky'); |
| 214 | +log(Nicky.name); // Nicky |
| 215 | +Nicky.name = 'Samantha'; |
| 216 | +log(Nicky.name); // Samantha |
| 217 | +log(Nicky[h_method]()); // I will hack you boy |
| 218 | +log(Object.keys(Nicky)); // [] - will not show the symbol method |
| 219 | +log(Reflect.ownKeys(Nicky)); // Shows all keys including symbol keys |
| 220 | + |
| 221 | + |
| 222 | + |
| 223 | + |
| 224 | + |
| 225 | +/* Tips: (relevant info) |
| 226 | +Classes are first-class citizens in JavaScript, meaning they can be passed around as you would any |
| 227 | +other object or function reference: |
| 228 | +// Classes may be defined anywhere a function would, such as inside an array: |
| 229 | +let classList = [ |
| 230 | + class { |
| 231 | + constructor(id) { |
| 232 | + this.id_ = id; |
| 233 | + console.log('instance ${this.id_}'); |
| 234 | + } |
| 235 | + } |
| 236 | +]; |
| 237 | +
|
| 238 | +function createInstance(classDefinition, id) { |
| 239 | + return new classDefinition(id); |
| 240 | +} |
| 241 | +
|
| 242 | +let foo = createInstance(classList[0], 3141); // instance 3141 */ |
| 243 | + |
| 244 | + |
| 245 | +/* Similar to an immediately invoked function expression, a class can also be immediately instantiated: |
| 246 | +// Because it is a class expression, the class name is optional |
| 247 | +let p = new class Foo { |
| 248 | + constructor(x) { |
| 249 | + console.log(x); |
| 250 | + } |
| 251 | +}('bar'); // bar |
| 252 | +
|
| 253 | +console.log(p); // Foo {} */ |
| 254 | + |
| 255 | + |
| 256 | +//Aditional Exercises |
| 257 | + |
| 258 | +//QUEUE |
| 259 | + |
| 260 | +class Queue { |
| 261 | + constructor(initialItems = []) { |
| 262 | + this.items = Array.isArray(initialItems) ? initialItems : []; |
| 263 | + } |
| 264 | + |
| 265 | + enqueue(element) { |
| 266 | + this.items.push(element); |
| 267 | + } |
| 268 | + |
| 269 | + dequeue() { |
| 270 | + if (this.isEmpty()) { |
| 271 | + console.error("Queue is empty. Cannot dequeue an element."); |
| 272 | + return null; |
| 273 | + } |
| 274 | + return this.items.shift(); |
| 275 | + } |
| 276 | + |
| 277 | + peek() { |
| 278 | + if (this.isEmpty()) { |
| 279 | + console.error("Queue is empty. Cannot peek."); |
| 280 | + return null; |
| 281 | + } |
| 282 | + return this.items[0]; |
| 283 | + } |
| 284 | + empty() { |
| 285 | + return this.items = []; |
| 286 | + } |
| 287 | + |
| 288 | + isEmpty() { |
| 289 | + return this.items.length === 0; |
| 290 | + } |
| 291 | + |
| 292 | + size() { |
| 293 | + return this.items.length; |
| 294 | + } |
| 295 | +} |
| 296 | + |
| 297 | +const queue2 = new Queue([45, 32, 16]); |
| 298 | +log('Initial queue2:', queue2.items); // [45, 32, 16] |
| 299 | + |
| 300 | +queue2.enqueue(77); |
| 301 | +log('After enqueueing 4:', queue2.items); // [45, 32, 16, 77] |
| 302 | + |
| 303 | +log('Peek:', queue2.peek()); // 45 |
| 304 | + |
| 305 | +log('Dequeue:', queue2.dequeue()); // 45 |
| 306 | +log('After dequeueing:', queue2.items); // [32, 16, 77] |
| 307 | + |
| 308 | +log('Dequeue all elements:'); |
| 309 | +while (!queue2.isEmpty()) { |
| 310 | + log('Dequeued:', queue2.dequeue()); |
| 311 | +} // or we can just empty the queue queue2.empty() |
| 312 | + |
| 313 | +log('Final queue2:', queue2.items); // [] |
| 314 | +log('Dequeue from empty queue2:', queue2.dequeue()); //Logs error: Queue is empty. Cannot dequeue an element. & Dequeue from empty queue2: null |
| 315 | + |
| 316 | +//STACK |
| 317 | + |
| 318 | +class Stack { |
| 319 | + constructor(initialItems = []) { |
| 320 | + this.items = Array.isArray(initialItems) ? initialItems : []; |
| 321 | + } |
| 322 | + |
| 323 | + push(element) { |
| 324 | + this.items.push(element); |
| 325 | + } |
| 326 | + |
| 327 | + pop() { |
| 328 | + if (this.isEmpty()) { |
| 329 | + console.error("Stack is empty. Cannot pop an element."); |
| 330 | + return null; |
| 331 | + } |
| 332 | + return this.items.pop(); |
| 333 | + } |
| 334 | + |
| 335 | + peek() { |
| 336 | + if (this.isEmpty()) { |
| 337 | + console.error("Stack is empty. Cannot peek."); |
| 338 | + return null; |
| 339 | + } |
| 340 | + return this.items[this.items.length - 1]; |
| 341 | + } |
| 342 | + |
| 343 | + empty() { |
| 344 | + return this.items = []; |
| 345 | + } |
| 346 | + |
| 347 | + isEmpty() { |
| 348 | + return this.items.length === 0; |
| 349 | + } |
| 350 | + |
| 351 | + size() { |
| 352 | + return this.items.length; |
| 353 | + } |
| 354 | +} |
| 355 | + |
| 356 | +const stack2 = new Stack([55, 76, 98, 100]); |
| 357 | +log('Initial stack2:', stack2.items); // [55, 76, 98, 100] |
| 358 | + |
| 359 | +stack2.push(32); |
| 360 | +log('After pushing 32:', stack2.items); // [55, 76, 98, 100, 32] |
| 361 | + |
| 362 | +log('Peek:', stack2.peek()); // 32 |
| 363 | + |
| 364 | +log('Pop:', stack2.pop()); // 32 |
| 365 | +log('After popping:', stack2.items); // [55, 76, 98, 100] |
| 366 | + |
| 367 | +log('Pop all elements:'); |
| 368 | +while (!stack2.isEmpty()) { |
| 369 | + log('Popped:', stack2.pop()); |
| 370 | +} // or we can just empty the stack stack2.empty() |
| 371 | + |
| 372 | +log('Final stack2:', stack2.items); // [] |
| 373 | +log('Pop from empty stack2:', stack2.pop()); // Logs error: Stack is empty. Cannot pop an element. & Pop from empty stack2: null |
0 commit comments