ООП в прототипном стиле JavaScript
Как мы знаем, в JavaScript есть два вида наследования:
– ООП в функциональном стиле;
– ООП в прототипном стиле;
Более популярным является прототипный стиль, поскольку имеет главное преимущество – методы в прототипе автоматически доступны везде и всегда. Почему? Методы записываются в объекте, ссылка на который находится в специальном свойстве prototype. Такое свойство есть у каждой функции – оно ссылается на объект с одним единственным свойством constructor:
function F() {};
F.prototype = {
constructor: F
};
Объекты-прототипы есть и у встроенных конструкторов (Object.prototype, Array.prototype и т.д) – в них хранятся служебные методы, например, toString, join, прочие.
Мы также всегда можем добавить свои методы в объект конструктор, например:
Object.prototype.countSomething = function() {...}
F.prototype.countSomething = function() {...}
И эти методы станут доступны новым объекту, который создаст конструктор F:
function F() {};
F.prototype.countSomething = function() {...};
var f = new F();
f.countSomething(); // will work!
Каким образом?
Свойство F.prototype буквально означает, что при запуске конструктора F будет создан новый объект, который получит ссылку f.__proto__ на объект-прототип со всеми его методами:
Вот и получается, что:
alert(f.__proro__ == F.prototype) // true;
alert(f.countSomething == F.prototype.countSomething); // true
alert(f.__proro__. countSomething == F.prototype.countSomething); // true
Обратите внимание на последние 2 строки: если метод countSomething() не нашёлся в самом объекте f, созданным конструктором F, поиск продолжается в его объекте-прототипе (f.__proro__).
Пойдём дальше и рассмотрим наследование конструкторов один от другого. Пускай, у нас есть 2 конструктора: Second будет наследовать от First:
function First() {...};
First.prototype = {...}; // здесь общие методы в объекте-прототипе
function Second() {...};
Second.prototype = {...}; // здесь конкретные методы в объекте-прототипе
var obj = new Second();
Алгоритм наследования такой: если нужный метод не найдёт в объекте obj, мы ищем в Second.prototype (объекте-прототипе конструктора, который создал obj); если нужного метода и там нет, ищем в First.prototype:
obj > Second.prototype > First.prototype.
Как мы уже знаем, при создании новый объект obj автоматически получает ссылку на объект-прототип: obj.__proto__ == Second.prototype. А как же заставить Second.prototype наследовать от First.prototype?
Можно, конечно, прописать такую конструкцию: Second.prototype.__proto__ = First.prototype, но на самом деле есть специальный метод:
Second.prototype = Object.create(First.prototype);
Он создаёт новый пустой объект с ссылкой на объект-прототип First.prototype. Далее мы смело можем добавлять нужные нам методы в Second.prototype. Итоговая последовательность наследования:
function First() {...}; // объявили общий конструктор
First.prototype = {...}; // добавили методы в его объект-прототип
function Second() {...}; // объявили конкретный конструктор
Second.prototype = Object.create(First.prototype); // создали пустой объект с ссылкой на прототип First.prototype
Second.prototype = {...}; // добавили методы в его объект-прототип
var obj = new Second(); // запустили конструктор