JavaScript · Синтаксис · Початковий рівень

ООП: успадкування та перевизначення методів

9 завдань

Завдання по ООП JavaScript на спадкування, super(), constructor, перевизначення методів, toString(), власні методи класів і роботу з об'єктами.

Успадкування — extends і super

#
Успадкування дозволяє одному класу повторно використовувати весь код іншого і потім розширювати або перевизначати конкретні частини. У JavaScript це робиться через extends: ```javascript class Animal { constructor(name) { this.name = name; } } class Dog extends Animal { bark() { return `${this.name} says woof`; } } const d = new Dog("Rex"); console.log(d.name); // Rex — успадковано від Animal console.log(d.bark()); // Rex says woof ``` Dog успадковує constructor від Animal, тому new Dog("Rex") працює без власного constructor. Dog також отримує всі інші методи Animal. Коли дочірній клас потребує власного constructor, він має викликати super() першим — до будь-якого звернення до this: ```javascript class Admin extends User { constructor(name, role) { super(name); // викликає constructor User this.role = role; } } ``` super() тут не опціональний — пропуск викличе ReferenceError при спробі використати this. Правило: якщо ви пишете constructor у підкласі, першим у ньому має бути super(). Перевизначення методу означає визначення методу з тим самим ім'ям у дочірньому класі. JavaScript завжди викликає найспецифічнішу версію.

toString() і власні методи

#
JavaScript-класи можуть перевизначати вбудовані методи, щоб контролювати поведінку об'єктів у стандартних операціях. toString() контролює те, що JavaScript повертає, коли об'єкт перетворюється на рядок — у шаблонних літералах, конкатенації або явному виклику String(): ```javascript class Book { constructor(title, pages) { this.title = title; this.pages = pages; } toString() { return `Book: ${this.title}, ${this.pages} pages`; } } const b = new Book("JavaScript Basics", 300); console.log(String(b)); // Book: JavaScript Basics, 300 pages console.log(`${b}`); // Book: JavaScript Basics, 300 pages ``` Без toString() вираз `${b}` виводив би [object Object] — стандартне представлення, яке рідко буває корисним. JavaScript не має вбудованого аналога Python's __len__. Щоб надати доступ до кількості елементів — визначте звичайний метод або геттер: ```javascript class Box { constructor(items) { this.items = items; } get length() { return this.items.length; } } const b = new Box([1, 2, 3]); console.log(b.length); // 3 — викликається як властивість, не b.length() ``` Геттери дозволяють зовнішньому коду читати обчислені дані, не знаючи, що це виклик методу.

Патерни успадкування і методів

#
Успадкування constructor батька і додавання нової поведінки — спочатку викликаємо super(), потім встановлюємо нові властивості: ```javascript class Playlist { constructor() { this.songs = []; } add(song) { this.songs.push(song); } remove(song) { const index = this.songs.indexOf(song); if (index !== -1) { this.songs.splice(index, 1); } } } class CustomPlaylist extends Playlist { constructor(name) { super(); // створює this.songs = [] this.name = name; // додає нову властивість } } ``` super() обов'язковий, коли constructor батька налаштовує стан, від якого залежить дочірній клас. Метод, що використовує Math.random() і успадковує всю іншу поведінку: ```javascript class MagicBox extends Box { randomItem() { const i = Math.floor(Math.random() * this.items.length); return this.items[i]; } } ``` Math.random() повертає float у [0, 1), тому Math.floor(Math.random() * n) дає випадкове ціле у [0, n-1]. Властивість зі значенням за замовчуванням у constructor: ```javascript class Message { constructor(text) { this.text = text; this.spam = false; } markSpam() { this.spam = true; } } ``` Властивості не обов'язково мають приходити з параметрів. spam починається як false для кожного нового Message — це правильне значення за замовчуванням.
01

Animal і Dog через наслідування.

#

Створіть клас Animal, у створюваних ним об’єктів має бути властивість name. Значення у властивість передається під час створення об’єкта через constructor. Тобто об’єкт описує тварину (будь-яку) і зберігає її кличку. Потім створіть клас Dog, який успадковується від Animal. У класі Dog додайте метод toString, який повертає рядок, у якому міститься тип тварини (ахахах, вибачаюсь, "тип тварини" звучить смішно) і її кличка: "Dog: Rex" # приклад рядка, що повертається Сенс такий: базовий клас Animal зберігає основну "поведінку" об’єктів тварин, а клас Dog деталізує під конкретний тип тварини.

class Animal {
    // ваш код тут
}
class Dog extends Animal {
    // ваш код тут
}
Рішення
class Animal {
    constructor(name) {
        this.name = name;
    }
}
class Dog extends Animal {
    toString() {
        return `Dog: ${this.name}`;
    }
}
02

Book із рядковим представленням.

#

Створіть клас Book із властивостями об’єктів title і pages. Додайте метод toString(), який повертає: "Book: JavaScript Basics (120 pages)" Можна також додати метод length(), який повертатиме кількість сторінок книги.

class Book {
    // ваш код тут
}
Рішення
class Book {
    constructor(title, pages) {
        this.title = title;
        this.pages = pages;
    }
    length() {
        return this.pages;
    }
    toString() {
        return `Book: ${this.title} (${this.pages} pages)`;
    }
}
03

User і Admin.

#

Давайте ще раз зробимо щось схоже на 1-ше: Умова: Створіть клас User із властивістю name. Створіть клас Admin, який успадковується від User. Перевизначте метод toString, щоб вивід був: "Admin: Alex" А у звичайного User через toString виводилось просто: "Alex"

class User {
    // ваш код тут
}
class Admin extends User {
    // ваш код тут
}
Рішення
class User {
    constructor(name) {
        this.name = name;
    }
    toString() {
        return this.name;
    }
}
class Admin extends User {
    toString() {
        return `Admin: ${this.name}`;
    }
}
04

Коробка з предметами.

#

Створіть клас Box із властивістю items_count. Додайте метод length(), який повертає кількість предметів. І додайте методи add і remove, які можуть класти й забирати об’єкт із коробки. Це можна зробити просто через лічильник items_count (просто рахувати без складнощів), або зробити масив предметів у коробці (рядків) і адаптувати клас для роботи з масивом (масив як властивість об’єкта).

class Box {
    // ваш код тут
}
Рішення
class Box {
    constructor(itemsCount) {
        this.itemsCount = itemsCount;
    }

    add(count) {
        if (count > 0) {
            this.itemsCount += count;
        }
    }

    remove(count) {
        if (count > 0) {
            this.itemsCount -= count;

            if (this.itemsCount < 0) {
                this.itemsCount = 0;
            }
        }
    }

    getCount() {
        return this.itemsCount;
    }
}
05

Message зі spam-прапорцем.

#

Створіть клас Message із властивостями text і spam. Значення text передається під час створення об’єкта через constructor, а spam спочатку дорівнює null. Додайте методи: isSpam() — перевіряє, чи є в тексті слова, схожі на spam, повертає true/false і записує результат у this.spam; length() — повертає довжину тексту.

class Message {
    // ваш код тут
}
Рішення
class Message {
    constructor(text) {
        this.text = text;
        this.spam = null;
        this.spamTriggers = ["куплю", "дорого"];
    }

    isSpam() {
        if (this.spam !== null) {
            return this.spam;
        }

        for (let trigger of this.spamTriggers) {
            if (this.text.toLowerCase().includes(trigger)) {
                this.spam = true;
                return true;
            }
        }

        this.spam = false;
        return false;
    }

    length() {
        return this.text.length;
    }
}

let m = new Message("Куплю старі телевізори дорого!!!");
console.log(m.isSpam());
console.log(m.length());
06

Employee і Manager.

#

Створіть клас Employee (працівник) із властивостями name і salary. Зробіть метод у Employee, який може змінювати зарплату. Створіть клас Manager, який успадковується від Employee. Перевизначте метод toString так, щоб вивід був: Manager John earns 5000 Переконайтеся, що метод зміни зарплати також працює для Manager

class Employee {
    // ваш код тут
}
class Manager extends Employee {
    // ваш код тут
}
Рішення
class Employee {
    constructor(name, salary) {
        this.name = name;
        this.salary = salary;
    }

    changeSalary(coef) {
        this.salary = Math.round(this.salary * coef);
    }
}

class Manager extends Employee {
    toString() {
        return `Manager ${this.name} earns ${this.salary}`;
    }
}

let manager = new Manager("Bob", 1000);

manager.changeSalary(1.1);

console.log(manager.toString());
07

Playlist і CustomPlaylist.

#

Створіть клас Playlist із властивістю songs (масив). Додайте методи add і remove для додавання та видалення пісень. Додайте метод reverse(), який перевертає масив треків. Додайте метод length(), який повертає кількість пісень. Додайте метод isEmpty(), який перевіряє, порожній плейлист чи ні. Потім створіть клас-нащадок CustomPlaylist, у якому метод add() дозволятиме додавати дублікати пісень у плейлист. У базовому Playlist дублікати додавати не можна.

class Playlist {
    // ваш код тут
}
Рішення
class Playlist {
    constructor(songs) {
        this.songs = songs;
    }

    add(song) {
        if (!this.songs.includes(song)) {
            this.songs.push(song);
        }
    }

    remove(song) {
        if (this.songs.includes(song)) {
            this.songs.splice(this.songs.indexOf(song), 1);
        }
    }

    reverse() {
        this.songs.reverse();
    }

    length() {
        return this.songs.length;
    }

    isEmpty() {
        return this.songs.length === 0;
    }
}

class CustomPlaylist extends Playlist {
    add(song) {
        this.songs.push(song);
    }
}
08

Іменований CustomPlaylist.

#

Допрацюйте попередній клас CustomPlaylist, додавши йому власний constructor. Він має викликати constructor батьківського класу через super(songs), а потім зберегти ім’я плейлиста у властивість name.

class CustomPlaylist extends Playlist {
    constructor(songs, name) {
        super(songs);
        // ваш код тут
    }

    add(song) {
        this.songs.push(song);
    }
}
Рішення
class Playlist {
    constructor(songs) {
        this.songs = songs;
    }

    add(song) {
        if (!this.songs.includes(song)) {
            this.songs.push(song);
        }
    }
}

class CustomPlaylist extends Playlist {
    constructor(songs, name) {
        super(songs);
        this.name = name;
    }

    add(song) {
        this.songs.push(song);
    }

    toString() {
        return `Playlist: ${this.name}`;
    }
}
09

MagicBox із випадковим предметом.

#

Давайте повернемося до класу Box (код нижче є). Давайте успадкуємося від нього й зробимо клас MagicBox. Єдина додана поведінка — це те, що під час створення об’єкта коробки в ній з’являтиметься випадковий предмет із масиву. Масив можливих випадкових предметів визначте де завгодно, це не важливо, хоч у глобальній області. Заготовка:

class Box {
    constructor() {
        this.items = [];
        this.itemsCount = this.items.length;
    }
    add(item) {
        this.items.push(item);
        this.itemsCount = this.items.length;
    }
    remove(item) {
        if (this.items.includes ? this.items.includes(item) : (item in this.items)) {
            this.items.splice(this.items.indexOf(item), 1);
            this.itemsCount = this.items.length;
        } else {
            console.log("Кіт із коробки каже: Такого тут немає, до побачення!");
        }
    }
    length() {
        return this.itemsCount;
    }
}
let things = ["Шапка", "Парасолька", "Кружка"];
class MagicBox extends Box {
    constructor() {
        // викличте super() і додайте випадковий предмет
    }
}
Рішення
function choice(items) {
    return items[Math.floor(Math.random() * items.length)];
}

class Box {
    constructor() {
        this.items = [];
        this.itemsCount = this.items.length;
    }

    add(item) {
        this.items.push(item);
        this.itemsCount = this.items.length;
    }

    remove(item) {
        if (this.items.includes(item)) {
            this.items.splice(this.items.indexOf(item), 1);
            this.itemsCount = this.items.length;
        } else {
            console.log("Кіт із коробки каже: Такого тут немає, до побачення!");
        }
    }

    length() {
        return this.itemsCount;
    }
}

const things = ["Шапка", "Парасолька", "Кружка"];

class MagicBox extends Box {
    constructor() {
        super();
        this.items.push(choice(things));
        this.itemsCount = this.items.length;
    }
}