Прототипы
В наших предыдущих примерах классов у нас было, как правило, три возможности для назначения свойств. Мы могли прицепить их прямо к объекту класса, например A.temp. Или же могли установить временные свойства внутри блока класса {}, используя выражение var x = 5. Мы также могли назначать свойства для объекта (this), "проходящего" сквозь объект активации класса, во время создания экземпляра. Такие свойства затем отображались в новом экземпляре. Казалось бы, вариантов для назначения свойств достаточно много, но всё же кое-что до сих пор оставалось для нас невозможным. Нет никакого способа назначать свойства, принадлежащие классу, но в то же время доступные для экземпляра. Наверное, это звучит странно, так что давайте рассмотрим такую ситуацию подробнее. Вот один простой пример, так сказать "собачий" (Dog): Dog = function() { this.legs = 4; } rover = new Dog(); fido = new Dog(); yeller = new Dog();
Сначала может показаться, что свойство this.legs принадлежит к классу Dog из-за того, что оно назначается там же. Можно подумать, что его специально "оставили доступным" для каждого экземпляра Dog и что это свойство, вместе с тем, является частью класса. Однако это не так. Свойство legs сказывается только на объекте экземпляра и нет никакой возможности добраться до него, если вы начнёте с Dog (его не находят даже такие выражения: Dog.instance.legs, Dog.benji.legs, Dog.legs). Определённо, это не свойство Dog. Это свойство экземпляра (benji.legs, fido.legs... - вот так пойдёт). А что если мы попробуем так: Dog.legs = 4 ? Может тогда это свойство станет свойством класса доступным для экземпляров? Если немного "поколдовать", экземпляры будут (иногда) иметь доступ к такому свойству (benji.constructor.legs - уж если вам так интересно), но правильно ли это? Должен ли benji "спрашивать" класс, определяющий Dog, сколько у него должно быть legs или он и так будет знать об этом? Если предположить, что benji - это тип (разновидность) собаки (Dog), то он (benji) должен иметь все собачьи (Dog) достоинства, которые должны распространяться на него автоматически. Если мы напишем Dog.legs = 4, то benji.legs останется равным 0. Нам поможет то, что называется объект-прототип.

Теперь можно поместить в собачий (Dog) класс информацию обо всех собаках и использовать экземпляры для информации, которая уникальна для каждой собаки. Осталось только выучить синтаксис, что обычно не представляет проблем: Dog = function( ){} Dog.prototype.legs = 4; rover = new Dog( ); fido = new Dog( ); yeller = new Dog( ); fido.puffyHair = true;
Теперь всё на своём месте - у всех Собак (Dog) по четыре лапы, но вот puffyHair - личный выбор fido. Однако, это не означает, что у остальных собак не может быть точно такой же "причёски". Просто данное свойство (puffyHair) специфично и индивидуально, оно не является общей особенностью собак вообще. Итак, у fido есть свойство puffyHair, у других его нет. У всех у них есть свойство legs, хотя оно пошло скорее от Dog.prototype.legs, чем от fido.legs. Каким образом у fido есть доступ к legs? Вот в чём вся прелесть! Обращение к такому свойству происходит так же, как и любое другое обращение к любому другому свойству fido - fido.legs. И вообще, мы не знаем, не хотим знать, да нам и не нужно знать, откуда берётся это свойство, что, разумеется, не означает, что нам не надо поставить и хранить его в специально отведённом для него месте во время работы нашей программы. Но обращаетесь вы к нему так, как будто это свойство экземпляра: rover, fido или yeller. Да, это замечательно, но возникает путаница с терминологией, поскольку мы уже привыкли называть объекты контейнерами и даже коробками. Давайте же немного проясним ситуацию. Мы можем представлять себе объекты, как коробки со списком их содержимого, прилепленным клейкой лентой к внешней стенке. Однако, происходящее внутри этих объектов вовсе не похоже на то, что может случиться с обычной картонной коробкой. Для этого нам потребуется новая метафора.


Посмотрите на изображение стёкол выше. Видите, как расположены квадратики каждого слоя? Жёлтые на первом слое, оранжевые - на втором, фиолетовые - на третьем.
- Вопрос:
Что бы вы делали, если бы теперь вам нужно было изменить центральный (фиолетовый) квадрат?
- Ответ:
Налепил бы новый бумажный квадратик сверху - а то слишком уж муторно расслаивать стёкла.
Таким образом, свойства объекта менее похожи на схематичные рисунки мелом на доске, а скорее на кусочки бумаги, приклеенные к многослойному оконному стеклу. Первый (передний) слой стекла - это наш экземпляр. Когда он создаётся, то помещает свойства на своём уровне. Свойства прикреплены к нему (или, точнее, к безымянному объекту, которым он станет) посредством использования ключевого слова this После того, как он создан, свойства также могут быть прикреплены вручную. Для этого можно использовать выражение instance.x=5. Далее мы рассмотрим ещё несколько способов появления свойств на слое экземпляра. Второй слой стекла содержит свойства, принадлежащие создавшему экземпляр классу. Они находятся в выражении Class.prototype. Что бы вы ни добавляли ко второму слою, всё это будет "видно" для всех экземпляров, созданных с помощью класса. Возвращаясь к экземпляру с собакой (Dog), если бы мы добавили свойство puffyHair к Dog.prototype (второй слой стекла), то все три собаки имели бы свойство puffyHair. Старый крикун (yeller), каким бы не был преданным и верным, вряд ли бы остался доволен.

<< ООП во Flash 5 >>