Към съдържанието
wannacode

Поведение

Проблем

Всеки тегли по своему

Банката има клас Account със собственик и баланс. Парите се теглят на две места в програмата. Банкоматът проверява дали има достатъчно пари. Плащането с карта е писано от друг човек и не проверява:

const account = new Account("Мария", 100);
// Банкоматът проверява дали има достатъчно пари.
if (account.balance >= 70) {
account.balance -= 70;
}
// Плащането с карта е писано от друг човек и проверка няма.
account.balance -= 50;
console.log(account.balance);
Изход
-20

Сметката е на минус. Правилото „не можеш да изтеглиш повече, отколкото имаш“ е важно, а е написано (или забравено) поотделно на всяко място, където се тегли.

Идеята

Методите живеят при данните

Логиката за теглене е част от сметката, затова я сложи в класа Account. Функция, написана в класа, е метод на обекта:

bank.js
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);
Стъпка по стъпка, с думи
  1. Две сметки, два обекта. Всеки има свой баланс.
  2. maria.withdraw(70): в this отива стрелка към обекта пред точката.
  3. В метода this.balance е балансът на Мария: 100 - 70 = 30.
  4. Същият метод, друг обект: сега this сочи Петър.
  5. 70 е повече от 40: методът печата отказ и балансът на Петър не се пипа.
  6. Кодът на метода е един. Полетата са на обекта, през който го викаш.

Плащането с карта вече не може да „забрави“ проверката, ако тегли с 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);
Отговор

Пробвай

Намери грешката

account.withdraw(30) спира програмата с грешка. На кой ред е грешката?

Избери реда с грешката.

Идеята

Методът може и да отговаря

Метод на обекта може да връща резултат, както функциите от модул „Функции“. Продуктът знае цената и бройката си, затова може сам да каже колко струва:

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);
Отговор

Упражнение

Обекти, които правят нещо

Реши задачите в редактора си. Вземи класовете от миналите уроци и им добави методи.

  1. Person: метод introduce(), който печата името и възрастта.
  2. SimpleCar: метод accelerate(), който увеличава скоростта с 10. Сега кодът под класа не пипа скоростта направо.
  3. DanceMove: метод perform(), който „изпълнява“ стъпката.
  4. 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));
Изход
Скорост: 20
Toyota: 100 км, 14 л
Няма достатъчно гориво за 300 км
Toyota: 100 км, 50 л
Toyota: 400 км, 32 л
Подсказка: distance * 6 / 100. В JavaScript / винаги дава дробно число, затова за 30 км излиза 1,8 литра.

Още задачи

  • Отстъпка. Добави към Product метод applyDiscount(percent), който намалява цената на продукта. Сравни го със функцията от модул „Функции“, която връщаше нова цена.
  • Сметка с история. Добави към Account брояч на операциите: всяко внасяне и успешно теглене го увеличава с 1. Метод toString() връща собственика, баланса и броя на операциите.

Тест

Провери се

1. Какво прави maria.deposit(30)?
2. Какво вижда методът, което функцията извън класа не вижда?
3. Защо Account.withdraw(70) спира с TypeError?
4. Кога JavaScript вика toString() сам?

Създаден от Иво Гергов като част от кампанията на Evo Tech за достъпно обучение по програмиране ·