Введение
В отличие от классических языков, таких как C++ и Java, JavaScript использует наследование на основе прототипов, а не наследование на основе классов. Это означает, что JavaScript разделяет свойства и методы через «прототипы». Хотя это может показаться нетрадиционным, этот уникальный аспект JavaScript во многом обеспечивает его гибкость и эффективность.
В этом посте мы подробно рассмотрим наследование прототипов, его применение, то, как его можно использовать для решения сложных проблем, и другие важные свойства, связанные с ним.
Постановка задачи
Давайте начнем с рассмотрения проблемы, в которой мы не используем наследование, и того, как мы можем столкнуться с проблемами избыточности кода.
Рассмотрим веб-приложение, которое поддерживает разные типы пользователей: Admin и Member. Каждый тип пользователей будет иметь общие свойства, такие как name и email, но они также будут иметь уникальные свойства и методы. У Admin может быть метод deleteUser, которого нет у Member.
function Admin(name, email) {
this.name = name;
this.email = email;
this.isAdmin = true;
}
Admin.prototype.deleteUser = function(user) {
// Code to delete a user
};
function Member(name, email) {
this.name = name;
this.email = email;
this.isAdmin = false;
}
Member.prototype.comment = function(commentContent) {
// Code to post a comment
};
И Admin, и Member имеют свойства name и email. Эта избыточность может привести к дублированию кода и проблемам с обслуживанием. Например, если мы решим добавить новое свойство, такое как birthdate, для всех пользователей, нам придется добавить это свойство как в конструкторы Admin, так и в конструкторы Member.
Реализация наследования прототипов
Чтобы решить эти проблемы избыточности, мы можем реализовать наследование прототипов, создав общий конструктор User, а затем наследуя Admin и Member от User:
function User(name, email) {
this.name = name;
this.email = email;
}
User.prototype.login = function() {
console.log(this.email + ' logged in');
};
function Admin(name, email) {
User.call(this, name, email);
this.isAdmin = true;
}
Admin.prototype = Object.create(User.prototype);
Admin.prototype.deleteUser = function(user) {
// Code to delete a user
};
function Member(name, email) {
User.call(this, name, email);
this.isAdmin = false;
}
Member.prototype = Object.create(User.prototype);
Member.prototype.comment = function(commentContent) {
// Code to post a comment
};
В этом коде и Admin, и Member наследуются от User, поэтому они оба имеют доступ к методу login. Если мы хотим добавить новое свойство или метод, которым должны делиться все пользователи, мы можем добавить его в User, и Admin и Member автоматически получат к нему доступ. Эта реализация устраняет избыточность кода и делает кодовую базу более удобной в сопровождении.
Расширенные свойства наследования прототипов
Переопределение метода
Подклассы могут переопределять методы своего родительского класса. Если метод определен для класса и его прототипа, метод, прикрепленный к самому классу, будет иметь приоритет. Это полезно, когда вы хотите, чтобы метод вел себя по-разному в подклассе.
Admin.prototype.login = function() {
console.log(this.email + ' logged in with admin privileges');
};
В этом случае Admin переопределяет метод login User. Когда login вызывается для экземпляра Admin, в сообщении будет указано, что пользователь вошел в систему с правами администратора.
Использование instanceof и isPrototypeOf
Оператор instanceof проверяет, является ли объект экземпляром определенного класса, а isPrototypeOf проверяет, существует ли объект в цепочке прототипов другого объекта.
let admin = new Admin('John', '[email protected]');
console.log(admin instanceof Admin); // logs: true
console.log(admin instanceof User); // logs: true
console.log(User.prototype.isPrototypeOf(admin)); // logs: true
Миксины
Примеси — это способ заставить классы совместно использовать определенное поведение вместо объявления этого поведения в одной точке иерархии наследования.
Если поведение не вписывается в иерархию наследования, лучше использовать миксин для совместного использования кода между классами.
let sayHiMixin = {
sayHi() {
console.log(`Hello ${this.name}`);
},
sayBye() {
console.log(`Bye ${this.name}`);
}
};
Object.assign(User.prototype, sayHiMixin); // copy the methods
let user = new User("John");
user.sayHi(); // logs: "Hello John"
Заключение
В этой статье мы подробно изучили наследование прототипов в JavaScript, его приложения и то, как его можно использовать для решения сложных задач.
Использование прототипов эффективно устраняет избыточность и делает наш код JavaScript более эффективным, чистым и удобным в сопровождении.
Помните, что, хотя эти функции могут быть полезными, они также могут привести к усложнению кода, поэтому очень важно использовать их с умом. Удачного кодирования!