Наследование прототипов

Последнее обновление: 17.11.2023

JavaScript поддерживает наследование, что позволяет нам при создании новых типов объектов при необходимости унаследовать их функционал от уже существующих. Однако нужно понимать, что наследование в JavaScript отличается от наследования в других распространенных и популярных языках типа Java, C++, C# и ряде других. В JavaScript наследование - это наследование объектов (а не наследование классов или типов), которое еще называют наследование прототипов или прототипное наследование.

Для создания объекта на основе некоторого прототипа применяется функция Object.create(), в которую передается наследуемый прототип:

const person = {
    name: "", 
    age: 0,  
    print: function(){ 
        console.log(`Name: ${this.name}  Age: ${this.age}`);
    }
};

const employee = Object.create(person);  // employee использует прототип объекта person

// получаем прототип
console.log(employee.__proto__);     // {name: "", age: 0, print: ƒ}

employee.name = "Tom";
employee.age = 39;
employee.print();    // Name: Tom  Age: 39

В данном случае объект employee создан на основе прототипа объекта person, по сути объект employee наследует прототип объекта person. Благодаря такому наследованию объект employee обладает всеми теми же свойствами и методами, которые определены в объекте person.

В дополнение объекты могут определять свои свойства и методы. Например:

const person = {
    name: "", 
    age: 0,  
    print: function(){ 
        console.log(`Name: ${this.name}  Age: ${this.age}`);
    }
};

const employee = Object.create(person);  // employee использует прототип объекта person

employee.name = "Tom";
employee.age = 39;
employee.company = "Google";  // новое свойство
// новый метод
employee.work = function(){ 
    console.log(`${this.name} works in ${this.company}`);
}
employee.print();    // Name: Tom  Age: 39
employee.work();    // Tom works in Google

В данном случае объект employee дополнительно определяет свойство company и метод work.

При необходимости можно переопределить унаследованные методы:

const person = {
    name: "", 
    age: 0,  
    print: function(){ 
        console.log(`Name: ${this.name}  Age: ${this.age}`);
    }
};
const employee = Object.create(person);

employee.name = "Tom";
employee.age = 39;
employee.company = "Google";
// переопределяем метод print
employee.print = function(){ 
    console.log(`Name: ${this.name}  Age: ${this.age} Company: ${this.company}`);
}
employee.print();    // Name: Tom  Age: 39  Company: Google

Здесь переопределяем функцию print, чтобы она также выводила компанию работника. Можно пойти дальше и увеличить цепочку наследования:

const person = {
    name: "", 
    age: 0,  
    print: function(){ 
        console.log(`Name: ${this.name}  Age: ${this.age}`);
    }
};
// объект employee наследует прототип объекта person
const employee = Object.create(person);
employee.company = "";

// объект manager наследует прототип объекта employee
const manager = Object.create(employee);
// переопределяем метод print
manager.print = function(){ 
    console.log(`Name: ${this.name}  Age: ${this.age}\nManager in ${this.company}`);
}
manager.name = "Bob";
manager.age = 43;
manager.company = "Microsoft";
manager.print();    // Name: Bob  Age: 43  
                    // Manager in Microsoft

Таким образом, получаем цепочку прототипов - person-employee-manager: employee наследует прототип от person, manager наследует прототип от employee

Вызов методов базового прототипа

Иногда может быть необходимо вызвать методы, которые определены в прототипе. Это может быть полезно для сокращения кода, уменьшения дублирования, особенно когда код переопределенного метода повторяет логику метода из прототипа. Получив прототип объекта, мы можем вызвать у него методы с помощью функции call():

const person = {
    name: "", 
    age: 0,  
    print: function(){ 
        console.log(`Name: ${this.name}  Age: ${this.age}`);
    }
};
// объект employee наследует прототип объекта person
const employee = Object.create(person);
employee.name = "Tom";
employee.age = 39;
employee.company = "Google";
// переопределяем метод print
employee.print = function(){ 
    this.__proto__.print.call(this);    // вызываем версию метода из person
    // Object.getPrototypeOf(this).print.call(this);  // альтернативный вариант
    console.log(`Company: ${this.company}`);
}

employee.print();   // Name: Tom  Age: 39
                    // Company: Google

В данном случае в переопределенном методе print у типа employee вызываем через прототип версию метода print из person.

Проверка наследования прототипов и Object.isPrototypeOf()

С помощью метода Object.isPrototypeOf() можно проверить, является ли объект прототипом другого объекта:

const person = {
    name: "",
    print: ()=>console.log("Name:", this.name)
};
const user = {
    name: "",
    print: ()=>console.log("Name:", this.name)
};

// объект employee наследует прототип объекта person
const employee = Object.create(person);

console.log(person.isPrototypeOf(employee));    // true
console.log(user.isPrototypeOf(employee));      // false

Здесь объект employee наследует прототип от person. Соответственно вызов person.isPrototypeOf(employee) возвратит true. А объект user не является прототипом для employee даже несмотря на то, что у него тот же набор методов и свойств.

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850