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 со спам-флагом.

#

Создайте класс Message со свойствами text и spam. Значение text передаётся при создании объекта через constructor, а spam изначально равно null. Добавьте методы: isSpam() — проверяет, есть ли в тексте слова, похожие на спам, возвращает 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;
    }
}