Поведение
Проблем
Всеки тегли по своему
Банката има клас Account със собственик и баланс. Парите се теглят на две места в програмата. Банкоматът проверява дали има достатъчно пари. Плащането с карта е писано от друг човек и не проверява:
const account = new Account("Мария", 100);
// Банкоматът проверява дали има достатъчно пари.if (account.balance >= 70) { account.balance -= 70;}
// Плащането с карта е писано от друг човек и проверка няма.account.balance -= 50;
console.log(account.balance);-20Сметката е на минус. Правилото „не можеш да изтеглиш повече, отколкото имаш“ е важно, а е написано (или забравено) поотделно на всяко място, където се тегли.
Идеята
Методите живеят при данните
Логиката за теглене е част от сметката, затова я сложи в класа Account. Функция, написана в класа, е метод на обекта:
class Account { owner; balance;
constructor(owner, balance) { this.owner = owner; this.balance = balance; }
deposit(amount) { this.balance += amount; }
withdraw(amount) { if (amount <= this.balance) { this.balance -= amount; } else { console.log(`Няма достатъчно пари: ${this.owner}`); } }}
const account = new Account("Мария", 100);account.withdraw(70);account.withdraw(50);account.deposit(20);console.log(account.balance);Няма достатъчно пари: Мария50- Методът се пише в класа без думата
function:withdraw(amount) { … }. - Вика се през обект и точка:
account.withdraw(70), както полетата. - Вътре
this.balanceе балансът на тази сметка, на обекта пред точката.this.е задължително: без негоbalanceне е полето. - Проверката е написана веднъж. Който тегли през метода, минава през нея.
const maria = new Account("Мария", 100);const peter = new Account("Петър", 40);maria.withdraw(70);peter.withdraw(70);console.log(maria.balance, peter.balance);Стъпка по стъпка, с думи
- Две сметки, два обекта. Всеки има свой баланс.
- maria.withdraw(70): в this отива стрелка към обекта пред точката.
- В метода this.balance е балансът на Мария: 100 - 70 = 30.
- Същият метод, друг обект: сега this сочи Петър.
- 70 е повече от 40: методът печата отказ и балансът на Петър не се пипа.
- Кодът на метода е един. Полетата са на обекта, през който го викаш.
Плащането с карта вече не може да „забрави“ проверката, ако тегли с withdraw. Може обаче още да пише направо в полето. Как да го спрем, ще видим в урока „Капсулация“.
Идеята
Метод или функция
Функциите от модул „Функции“ стоят извън класа. Те получават всичко като параметри и нямат свой обект. Методът работи с полетата на обекта, през който е извикан:
function applyDiscount(price, percent) { return price * (100 - percent) / 100;}
const price = applyDiscount(10, 10);account.withdraw(70);Метод на обекта не можеш да извикаш през името на класа. Не е ясно от чия сметка да тегли:
Account.withdraw(70);TypeError: Account.withdraw is not a functionМетодът withdraw е на обектите, а не на самия клас Account. Методи на класа също има: пред тях стои static. Ще ги видим в урока „static: общото за всички“.
Пробвай
Чия сметка?
Какво ще отпечата програмата?
const maria = new Account("Мария", 100);const peter = new Account("Петър", 40);maria.deposit(30);peter.withdraw(10);console.log(maria.balance, peter.balance);Всеки обект има свой баланс. maria.deposit(30) сменя само баланса на Мария, а peter.withdraw(10) само на Петър. Петър има 40, затова тегленето на 10 минава.
Пробвай
Намери грешката
account.withdraw(30) спира програмата с грешка. На кой ред е грешката?Избери реда с грешката.
Без this. името balance е обикновена променлива, а такава няма:
ReferenceError: balance is not definedПолето е this.balance. Следващият ред го пише правилно, затова грешката е само в проверката.
Идеята
Методът може и да отговаря
Метод на обекта може да връща резултат, както функциите от модул „Функции“. Продуктът знае цената и бройката си, затова може сам да каже колко струва:
class Product { // полетата и конструкторът, както преди
total() { return this.price * this.quantity; }}
const cart = [ new Product("Хляб", 1.20, 2), new Product("Мляко", 2.10, 1), new Product("Кафе", 7.50, 1),];let sum = 0;for (const p of cart) { sum += p.total();}console.log(`Общо: ${sum} €`);Общо: 12 €Сравни с първия урок от модула: там сумата се смяташе в кода под класа от полетата. Сега този код само пита всеки продукт. Ако утре се добави отстъпка за количество, променяш само total().
Идеята
Как изглежда обектът като текст?
Отпечаташ ли продукт направо, излиза нещо безполезно:
const coffee = new Product("Кафе", 7.50, 1);console.log(`Покупка: ${coffee}`);console.log(coffee);Покупка: [object Object]Product { name: 'Кафе', price: 7.5, quantity: 1 }Като текст обектът е [object Object]. console.log на самия обект показва полетата му, но това е за проверка, докато пишеш. Текст, който да сложиш в изречение, това не е.
Класът може да каже как да се показва. Методът се казва toString() и връща текст:
class Product { // полетата и конструкторът, както преди
total() { return this.price * this.quantity; }
toString() { return `${this.name} x${this.quantity}: ${this.total()} €`; }}
const coffee = new Product("Кафе", 7.50, 1);const bread = new Product("Хляб", 1.20, 2);console.log(String(coffee));console.log(`В количката: ${bread}`);Кафе x1: 7.5 €В количката: Хляб x2: 2.4 €Не го викаш сам. String(coffee), шаблонният низ `В количката: ${bread}` и "В количката: " + bread го викат вместо теб, когато трябва обектът да стане текст. Само console.log(coffee) не го вика: той пак показва полетата.
Името трябва да е точно toString, с малко t и главно S. Откъде обектът има toString, още преди да го напишеш, ще разберем в модул „Наследяване и полиморфизъм“.
Пробвай
Книга в изречение
Какво ще отпечата програмата?
class Book { title; author;
constructor(title, author) { this.title = title; this.author = author; }
toString() { return `${this.title} от ${this.author}`; }}
const book = new Book("Тютюн", "Димитър Димов");console.log("Чета: " + book);"Чета: " + book трябва да направи book на текст. Затова се вика toString() на книгата, а тя връща заглавието и автора.Упражнение
Обекти, които правят нещо
Реши задачите в редактора си. Вземи класовете от миналите уроци и им добави методи.
Person: методintroduce(), който печата името и възрастта.SimpleCar: методaccelerate(), който увеличава скоростта с 10. Сега кодът под класа не пипа скоростта направо.DanceMove: методperform(), който „изпълнява“ стъпката.Book: методread(), който печата „Прочитане на [заглавие] от [автор]“.
Създай по един обект от всеки клас и извикай методите му:
Здравей, аз съм Мария и съм на 34 г.Скорост след ускоряване: 10Изпълнявам moonwalk за 5 секундиПрочитане на Под игото от Иван ВазовУпражнение
Кола с гориво
Клас Car с марка, скорост, гориво (в литри) и изминати километри. Конструкторът получава марката и горивото, скоростта и километрите започват от 0.
accelerate()увеличава скоростта с 10.refuel(liters)долива гориво, но резервоарът побира най-много 50 литра.drive(distance)харчи по 6 литра на 100 км и добавя разстоянието към километрите. Ако горивото не стига, печата съобщение и колата не тръгва.toString()връща марката, километрите и горивото.
const car = new Car("Toyota", 20);car.accelerate();car.accelerate();console.log(`Скорост: ${car.speed}`);car.drive(100);console.log(String(car));car.drive(300);car.refuel(40);console.log(String(car));car.drive(300);console.log(String(car));Скорост: 20Toyota: 100 км, 14 лНяма достатъчно гориво за 300 кмToyota: 100 км, 50 лToyota: 400 км, 32 лdistance * 6 / 100. В JavaScript / винаги дава дробно число, затова за 30 км излиза 1,8 литра.Още задачи
- Отстъпка. Добави към
ProductметодapplyDiscount(percent), който намалява цената на продукта. Сравни го със функцията от модул „Функции“, която връщаше нова цена. - Сметка с история. Добави към
Accountброяч на операциите: всяко внасяне и успешно теглене го увеличава с 1. МетодtoString()връща собственика, баланса и броя на операциите.
Тест