Прототипы в JavaScript
Как прототипы в JavaScript влияют на время выполнения кода?
Во времена своего обучения в университете, я заметил, что многие однокурсники, занимающиеся разработкой веб-приложений, не могут понять: зачем и как использовать прототипы в JavaScript.
Неспособность разобраться с этой проблемой крылась в предшествующем изучении C# и Java - языков, ориентированных на использование классов. Для того чтобы создать определенную логику приложения, приходилось начинать с создания классов со своими свойствами и методами. В свою очередь, свойства использовались для хранения значений, а методы - для совершения определенных действий.
Но JavaScript отличается и изначально не поддерживает такой функционал, так как является прототипо-ориентированным языком. Несомненно, ECMAScript 2015 в свое время представил несколько нововведений, позволяющих создавать классы и объекты, но это не более чем “синтаксический сахар”. Вот почему я хочу продемонстировать, как создавть классы и их экземпляры, не прибегая к ключевому слову class
, и как правильно пользоваться прототипами.
Итак, приступим.
Для того, чтобы объявить класс необходимо всего лишь создать функцию (хорошей практикой будет использование верблюжьей нотации). В нашем примере класс называется Person:
function Person(name, lastName) {
this.name = name;
this.lastName = lastName;
this.tellFullName = function() {
console.log(this.name + ' ' + this.lastName);
}
}
Теперь создадим новый объект: new Person('Alex', 'Gilbert');
. Полагаю, Вы спросите: “Зачем нужны прототипы, если у нас уже есть класс со всем необходимым функционалом?” Конечно, такое утверждение будет верным, до тех пор пока мы не обнаружим, что каждый новый экземпляр класса Person при создании получит в свое подчинение все свойства и методы, которые, в свою очередь, будут занимать новые области памяти. А что делать, если мы хотим использовать один и тот же метод tellFullName класса Person для всех его экземпляров?
Прототипы: мы должны переподчинить методы и свойства, которые не должны изменяться при создании объекта, прототипу класса Person:
function Person(name, lastName) {
this.name = name;
this.lastName = lastName;
}
Person.prototype.tellFullName = function() {
console.log(this.name + ' ' + this.lastName);
}
var p = new Person('Alex', 'Gilbert');
p.tellFullName(); // результат - 'Alex Gilbert'
В JavaScript каждый объект имеет свою цепочку прототипов. Кроме того, у прототипов могут быть свои прототипы. Такой подход позволяет управлять наследованием и переназначать необходимые свойства и методы прототипам более низкого уровня.
Наконец, давайте оценим эффективность использования различных экземпляров класса.
Создадим два различных объекта:
- класс с прототипами - WithPrototyping
-
класс без прототипов - NoPrototyping
const methodsAmount = 1000; const instancesAmount = 3000; function NoPrototyping() { for (let i = 1; i <= methodsAmount; i++) { this['method_' + i] = function () { return 'From method ' + i; } } } function WithPrototyping() { } console.time('NO_PROTOTYPING_TIME'); for (let i = 1; i <= instancesAmount; i++) { new NoPrototyping(); } console.timeEnd('NO_PROTOTYPING_TIME'); console.time('WITH_PROTOTYPING_TIME'); for (let i = 1; i <= methodsAmount; i++) { WithPrototyping.prototype['method_' + i] = function () { return 'From method ' + i; } } for (let i = 1; i <= instancesAmount; i++) { new WithPrototyping(); } console.timeEnd('WITH_PROTOTYPING_TIME');
Получены следующие результаты:
- NO_PROTOTYPING_TIME: 885.98193359375ms
- WITH_PROTOTYPING_TIME: 0.743896484375ms
Можно сделать вывод, что переподчинение свойств и методов экземпляра класса прототипу позволяет увеличить скорость работы приложения.
Будьте внимательны. Большие цепочки прототипов уменьшают производительность приложения из-за расходования ресурсов на процесс поиска.
Еще больше информации о наследовании и цепочках прототипов можно узнать здесь
Спасибо за внимание.