Section 14: Object-Oriented Programming (OOP) Flashcards
Definition
1/ OOP
2/ Traditional OOP’s properties
3/ Fundamental OOP’s principles
1/ OOP is a programming paradigm, it models a system as a collection of objects where each object represents an aspect of a system. Objects contain data (properties) and functions (methods). An object has a public interface for other code to use but maintain its own private, internal state. Other part of the code don’t have to care about what happens inside the object
2/ Class - instance
3/
- Abstraction: Hiding details that don’t matter. Giving an overview persective of the thing we’re implementing
- Encapsulation: Keeping properties and methods private inside a class so that they can’t be accessible from outside the class. Some methods can be exposed as public interface (API)
- Inheritance: Making all properties and methods of a class available to a child class, forming a hierarchial relationship between classes. This allows us to resue common logic and model real-world relationships
- Polymorphism: A child class can overide a method it inherited from a parent class
Difference
Traditional OOP vs OOP in JavaScript
- Traditional OOP: Objects are instantiated from a class
- OOP in JS: objects are inherited from prototype (Prototypal Inheritance): The prototype contains methods that are accessible to all objects linked to that prototype
How to implement Prototypal Inheritance
Using 4 OOP’s principles, there are 3 ways
- Constructor function
- ES6 Classes
- Object.create() method
What happens inside a constructor function (behind the scene)
- When calling function with new operator, an empty object { } is created
~~~
const Person = function (firstName, birthYear) {
};
new Person(‘Vu’, 1996);
~~~ -
this
keyword points to the empty object { }
~~~
const Person = function (firstName, birthYear) {
console.log(this); // Person{}
};
new Person(‘Vu’, 1996);
~~~ - Object { } links to prototype
~~~
const Person = function (firstName, birthYear) {
this.firstName = firstName;
this.birthYear = birthYear;
};
new Person(‘Vu’, 1996);
~~~ - Function automatically return object
~~~
const Person = function (firstName, birthYear) {
this.firstName = firstName;
this.birthYear = birthYear;
};
const vu = new Person(‘Vu’, 1996);
console.log(vu); // Person{firstName: ‘Vu’, birthYear: 1996}
~~~
Problem
/*
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;
2. Implement an ‘accelerate’ method that will increase the car’s speed by 10, and log the new speed to the console;
3. Implement a ‘brake’ method that will decrease the car’s speed by 5, and log the new speed to the console;
4. Create 2 car objects and experiment with calling ‘accelerate’ and ‘brake’ multiple times on each of them.
DATA CAR 1: ‘BMW’ going at 120 km/h
DATA CAR 2: ‘Mercedes’ going at 95 km/h
GOOD LUCK 😀
*/
// 1 const Car = function (make, speed) { this.make = make; this.speed = speed; }; // 2 Car.prototype.accelerate = function () { this.speed += 10; console.log(`New speed: ${this.speed} km/h`); }; // 3 Car.prototype.brake = function () { this.speed -= 5; console.log(`New speed: ${this.speed} km/h`); }; // 4 const bmw = new Car('BMW', 120); const mercedes = new Car('Mercedes', 95);
Definition
ES6 Classes
- Template to create object
- not hoisted
- first-class citizen (like function): can pass into other function or return from a function
- are executed in strict mode
Problem
/* Given an account object
const account = {
owner: ‘Jonas’,
movements: [100, 200, -300, 500],
}
Using 2 solution,
1. Extract last element of movements without having [ ]
2. Add a new value to movements
3. What is the difference between 2 solutions?
*/
const account = { owner: 'Jonas', movements: [100, 200, -300, 500], // 1 get getLastest() { return this.movements.slice().pop(); }, // 2 set getLastest(val) { return this.movements.push(val); }, }; console.log(account.getLastest); console.log(account.movements); account.getLastest = 10; console.log(account.getLastest); console.log(account.movements); // 3. getter + setter acts like a property, can use same name in methods
Definition
Getter and Setter
Get: The get syntax binds an object property to a function that will be called when that property is looked up. It can also be used in classes.
Set: The set syntax binds an object property to a function to be called when there is an attempt to set that property. It can also be used in classes.
All objects have setter and getter properties (assessor properties). Normal properties are called data properties
Problem
Create Person class takes full name and birth year as parameters.
- Add calcAge method and add getter function
- Check if name is full name or not, if not, alert error to browser.
- log full name to console
- What happens if we use this code? Explain
set fullName(name) {
if (name.includes(‘ ‘)) this.fullName = name;
else alert(${name} is not a full name
);
}
class Person {
constructor(fullName, birthYear) {
this.fullName = fullName;
this.birthYear = birthYear;
}
get calcAge() {
return 2023 - this.birthYear;
}
set fullName(name) {
if (name.includes(‘ ‘)) this._fullName = name;
else alert(${name} is not a full name
);
}
get fullName() {
return this._fullName;
}
}
const vu = new Person(‘Vu S’, 1996);
console.log(vu);
console.log(vu.fullName);
// 4. Constructor is calling the setter (this.fullName is now the setter function) and the setter is now calling … HIMSELF => infinite loop !! that’s why the error: Maximum call stack size!
Problem
/*
1. Re-create challenge 1, but this time using an ES6 class;
2. Add a getter called ‘speedUS’ which returns the current speed in mi/h (divide by 1.6);
3. Add a setter called ‘speedUS’ which sets the current speed in mi/h (but converts it to km/h before storing the value, by multiplying the input by 1.6);
4. Create a new car and experiment with the accelerate and brake methods, and with the getter and setter.
DATA CAR 1: ‘Ford’ going at 120 km/h
GOOD LUCK 😀
*/
“class Car {
constructor(make, speed) {
this.make = make;
this.speed = speed;
}
accelerate() {
this.speed += 10;
console.log(${this.make} speeds up at ${this.speed}
);
}
brake() {
this.speed -= 5;
console.log(${this.make} is slowed down at ${this.speed}
);
}
// 1
get speedUS() {
return this.speed / 1.6;
}
// 2
set speedUS(speed) {
this.speed = speed * 1.6;
}
}
const ford = new Car(‘Ford’, 120);
console.log(ford.speedUS); // 75 mi/h
ford.speedUS = 50;
console.log(ford); // 80 km/h”
Problem
/*
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);
2. Implement a ‘chargeBattery’ method which takes an argument ‘chargeTo’ and sets the battery charge to ‘chargeTo’;
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%’;
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 😉
DATA CAR 1: ‘Tesla’ going at 120 km/h, with a charge of 23%
GOOD LUCK 😀
Test with this
tesla.brake();
tesla.accelerate();
tesla.chargeBattery(90);
tesla.accelerate();
// Tesla is going to 155 km/h, with a charge of 89
*/
const Car = function (make, speed) {
this.make = make;
this.speed = speed;
};
Car.prototype.accelerate = function () {
this.speed += 20;
this.charge–;
console.log(${this.make} going at ${this.speed} km/h
);
};
Car.prototype.brake = function () {
this.speed -= 5;
console.log(${this.make} going at ${this.speed} km/h
);
};
const EV = function (make, speed, charge) {
Car.call(this, make, speed);
this.charge = charge;
};
// Linking
EV.prototype = Object.create(Car.prototype);
// Methods
EV.prototype.chargeBattery = function (chargeTo) {
this.charge = chargeTo;
};
EV.prototype.accelerate = function () {
this.speed += 20;
this.charge–;
console.log(
${this.make} going at ${this.speed} km/h, with a charge of ${this.charge}
);
};
const tesla = new EV(‘Tesla’, 120, 23);
tesla.brake();
tesla.accelerate();
tesla.chargeBattery(90);
tesla.accelerate();
Problem
/*
Problem
1. Create PersonCl class
- takes full name and birth year.
- Get calc age method,
- calc age as property,
- greet function to log “Hey Vu”,
- full name validation, - static method log “Hey there”
- Create StudentCl and perform objects linking.
- Implement introduce function to log “My name is Vu and I study JS”,
- implement calc age to log “I am 27 years old but as a student I feel more like 37”.
- When an object of StudentCl is create, which calc age function will it use? Why?
*/
"class PersonCl { constructor(fullName, birthYear) { this.fullName = fullName; this.birthYear = birthYear; } // Calc age method calcAge() { console.log(2023 - this.birthYear); } // Calc age property get age() { console.log(2023 - this.birthYear); } greet() { console.log(`Hey ${this.fullName}`); } // full name validation using Setter set fullName(name) { if (name.includes(' ')) this._fullName = name; else alert(`${name} is not a full name`); } get fullName() { return this._fullName; } // Static method static hey() { console.log(`Hey there`); } } const vu = new PersonCl('Vu Son', 1996); console.log(vu); class StudentCl extends PersonCl { constructor(fullName, birthYear, course) { super(fullName, birthYear); this.course = course; } introduce() { console.log(`My name is ${this.fullName} and I study ${this.course}`); } calcAge() { console.log( `I'm ${ 2023 - this.birthYear } years old, but as a student I feel more like ${ 2023 - this.birthYear + 10 }` ); } } const shin = new StudentCl('Shin Vu', 2017, 1); console.log(shin); shin.introduce(); shin.calcAge(); shin.age;"
Problem
/* From ‘Bankist’ Project, create an Account class so that we can have many sub account objects. It must have :
1. owner name
2. currency
3. PIN
4. a movements array to store deposit/withdraw values
5. a locale
6 + 7 method to push deposit/withdraw values to movements
8 + 9 Can provide a loan and top up to account (Hint: must have a approve loan function return to true to approve the loan first)
A. Done things above and answer this
1. Which argument should be included in constructor, which doesn’t. Why?
Which information is common in all objects
2. Should we use push method directly in Global Variable? What can be done?
3. Does deposit and withdraw method violate DRY principle?
4. Should user be allowed to access/manipulate PIN, movements, approveLoan ?
B. Encapsulation
1. Which data is public fields? Implement
2. Which data is private fields? Implement
3. Which data is public methods? Implement
4. Which data is private methods? Implement
5. Write a function to access and only view movements
*/
// ============== Problem A
class Account {
constructor(owner, currency, pin) {
this.owner = owner;
this.currency = currency;
this.pin = pin;
this.movements = [];
this.locale = navigator.language;
}
deposit(val) {
this.movements.push(val);
}
withdraw(val) {
this.deposit(-val);
}
approveLoan(val) {
return true;
}
requestLoan(val) {
if (this.approveLoan(val) this.deposit(val);
}
}
const acc1 = new Account(‘Vu’, ‘USD’, 1111);
acc1.deposit(500);
acc1.withdraw(200);
// acc1.movements.push(100);
// acc1.movements.push(-50);
acc1.requestLoan(1000);
console.log(acc1);
// ============== Problem B
class Account {
// Public fields
locale = navigator.language;
// Private fields
#movements = [];
#pin;
constructor(owner, currency, pin) {
this.owner = owner;
this.currency = currency;
this.#pin = pin;
}
// Public methods
accessMovements() {
return this.#movements;
}
deposit(val) {
this.#movements.push(val);
}
withdraw(val) {
this.deposit(-val);
}
requestLoan(val) {
if (this.#approveLoan(val)) this.deposit(val);
}
// Private methods
#approveLoan(val) {
return true;
}
}
Problem
/*
A)
1. Create CarCl class. A car has a make and a speed property. The speed property is the current speed of the car in km/h;
2. Implement an ‘accelerate’ method that will increase the car’s speed by 10, and log the new speed to the console like “BMW is going to 200 km/h”;
3. Implement a ‘brake’ method that will decrease the car’s speed by 5, and log the new speed to the console;
4. Add a getter called ‘speedUS’ which returns the current speed in mi/h (divide by 1.6);
5. Add a setter called ‘speedUS’ which sets the current speed in mi/h (but converts it to km/h before storing the value, by multiplying the input by 1.6);
B)
1. Implement an Electric Car (called EVCl) as a CHILD “class” of CarCl. Besides a make and current speed, the EV also has the current battery charge in % (‘charge’ property);
2. Implement a ‘chargeBattery’ method which takes an argument ‘chargeTo’ and sets the battery charge to ‘chargeTo’;
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%’;
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 😉
5. Make the ‘charge’ property private;
6. Implement the ability to chain the ‘accelerate’ and ‘chargeBattery’ methods of this class, and also update the ‘brake’ method in the ‘CarCl’ class. Then experiment with chaining!
DATA CAR 1: ‘Rivian’ going at 120 km/h, with a charge of 23%
*/
class CarCl { constructor(make, speed) { this.make = make; this.speed = speed; } accelerate() { this.speed += 10; console.log(`${this.make} is going to ${this.speed} km/h`); } brake() { this.speed -= 5; console.log(`${this.make} is going to ${this.speed} km/h`); return this; } get speedUS() { return this.speed / 1.6; } set speedUS(val) { this.speed = val * 1.6; } } class EVCl extends CarCl { #charge; constructor(make, speed, charge) { super(make, speed); this.#charge = charge; } chargeBattery(chargeTo) { this.#charge = chargeTo; return this; } accelerate() { this.speed += 20; this.#charge--; console.log( `${this.make} is going to ${this.speed} km/h, with a charge of ${ this.#charge } ` ); return this; } } const rivian = new EVCl('Rivian', 120, 23); console.log(rivian); rivian .accelerate() .accelerate() .accelerate() .brake() .chargeBattery(50) .accelerate(); // Rivian is going at 195 km/h, with a charge of 49 console.log(rivian.speedUS); // 121.875
Definition
1/ Promise
2/ Promise Methods
3/ Lifecycle
- Is an object that is used as a placeholder for future result of an asynchronous task
- then(), catch(), finally(),…
- Pending -> Settled (fulfilled or rejected)