Про то еще типы
[письмо]
Спасибо за Вашу статью по движению, как всегда очень все понятно. Я так обнаглел, что решил еще попросить - не могли бы Вы мне доступным языком объяснить, что такое прототипы и, главное, для чего они? Во многих исходниках их встречаю, но что-то никак не могу разложить в голове по полочкам :) Спасибо заранее!
[/письмо]
Эээх! прототипы! Про то еще типы. Действительно с непривычки голову можно сломать. Особенно радует, что объясняют про них как правило оч мудрёно. Что-ж, попробуем иначе.
Все мы знаем что такое мувик. У всех мувиков есть функции и методы, например: gotoAndPlay(), а так-же свойства, например _xscale. А еще мувики могут хранить в себе переменные и пользовательские функции. И это понятно.
Но бывают ситуации, когда необходимо, чтобы все мувики имели некую пользовательскую функцию.
В руте в новом FLA, в первом и единственном кадре пишем:
function myTrace() { // задаем функцию
trace(this); // просим вывести в окно output сыылку на мувик
}
this.myTrace() // вызываем функцию
и действиельно, например такая, не очень мудреная функция может понадобиться в процессе отладки. Но если создать мувик (создайте и обзовите экземпляр на сцене как-нибудь, я его назвал mc, но можно и dj (хе-хе) ) и в этом мувике написать:
onClipEvent (load) {
this.myTrace()
}
то помимо самого первого трейса ничего не будет. Потому как нету в этом мувике такой функции. Дык что теперь, чтобы вызвать функцию каждый раз ее прописывать надо?
- вот тут-то и пригодятся страшные и ужасные прототипы.
заменим в руте определение функции на:
MovieClip.prototype.myTrace = function () { // задаем функцию в прототипе мувиклипа
trace(this); // просим вывести в окно output сыылку на мувик
}
this.myTrace() // вызываем функцию
тестанем Ctrl+Enter.
Ух-ты, мувик mc (он-же dj (хе-хе)) отозвался!!. Если вы случайно не удалили вызов myTrace в нем %).
Теперь стоит пойти попить чаю (совет для некурящих).
Это что-же получается?
Мы в мувике функцию не задавали, а она там есть! Но нет. на самом деле ее там нет. Функция теперь лежит у нас в прототипе мувиклипа. Опа. А что же произошло?
- мувик mc порылся у себя, и не нашел этой функции. Но на этом его поиски не закончились!!! Мувик полез в поискать в чёрный ящик своего класса (класс у мувика - MovieClip), который называется прототип. И этот черный ящичек имеестя у всех объектов, а не только у мувиклипов.
Вывод первый: если что-нибудь положить в прототип мувиклипа, то это должно найтись без дополнительных усилий с нашей стороны. Нужно просто позвать, мувик сам найдет. И не только мувик, но и любой объект.
Object, Array, String, Number.... продолжите список сами. У них у всех есть черный ящичек по имени прототип. И мы уже обнаружили первое свойство прототипа. Он доступен всем объектам класса и его легко использовать.
Доступен всем объектам класса.... меня понесло, извините. Попросту говоря, если мы пихаем функцию, свойство или переменную в прототип мувиклипа, то найти это может мувиклип. Массив не найдет. И xml объект тоже не найдет.
А что произойдет, если такая функция, свойство или переменная уже имеется у мувика? Помотрим:
в мувике добавим определение функции:
onClipEvent (load) { // при загрузке
function myTrace (){ // задаем функцию
trace(this+" is busy") // вывода сообщения линк на мувик+" is busy"
}
this.myTrace() // и вызываем функцию
}
и что мы видим?
Мы видим вывод второй: как только мувик находит, нужную функцию, то тут же перестает искать дальше. Поскольку ищет мувик всегда вначале у себя, и если функция с таким именем уже есть просто не добирается до прототипа.
А если нам нужна функция, которую бы могли видеть все объекты, а не только мувики? И это возможно. Дело в том, что все объекты являются дочерними объектами класса Object, простите, меня опять понесло...
У людей есть Адам, сиречь пра-прародитель. У объектов во флеше тоже есть свой пра-прародитель. Имя ему Object. И от него произошли все объекты. И наследовали объекты от него все его умения. И также, как и наш незабвенный mc в случае, если не могут найти у себя функцию, свойство или переменную, ищут ее у своего родителя - у Object.
Поэкспериментим чуток, заменим месторасположение функции, поместим ее в Object:
Object.prototype.myTrace = function () { // задаем функцию в прототипе мувиклипа
trace("> "+ this+" is " + typeof this +" called from Object prototype"); // сделаем информативней наш трейс ([объект] это [тип объекта] вызван из прототипа Object)
}
this.myTrace() // вызываем функцию
видимый результат не изменился. Но теперь эту функцию можно вызывать откуда угодно, попробуем вызвать ее из массива и XML, ниже допишем:
this.myArray=[1, 2, 3] // создаем массив со значениями 1, 2, 3
this.myArray.myTrace() // вызываем из массива наш трейс
this.myXML = new XML ("<xml><item>Hello world</item></xml>")// создаем новый XML
this.myXML.myTrace()// вызываем из него наш трейс
ага, вот вот... параллельно понимаем, что такое this :o)
Итак, и xml и массив нашли наш трейс. Как это произошло? Последовательность такая:
1) поискали у себя - не нашли, ищем дальше
2) поискали в прототипе класса (myArray в Array.prototype, а myXML в XML.prototype) - не нашли, ищем дальше
3) поискали в классе - не нашли, ищем дальше
4) раз нет в нашем классе, то может в родительском классе есть? ищем в прототипе родительского класса: бинго!
это похоже на простую жизненную ситуацию:
- а нет ли у тебя какой-нибудь фигни, например, клюшки для гольфа?
ищем так:
у меня нет
у родителей в прихожей нет
у родителей в доме нет
а у дедушки оказалась в прихожей!
в поисках мы последовательно обследуем прототипы затем сами классы всё выше и выше забираясь по генеалогическому древу. До Адама, то-есть до Object.
Вывод третий: чем выше в иерархии классов помещаем функцию, тем большему количеству классов она доступна и наоборот.
О какой иерархии может идти речь, если все стандартные флешовые объекты являются детьми Object? Дело в том, что вы можете создавать свои классы, которые будут являться наследниками стандартных, а так-же пользовательских классов. А если проще сказать, вы можете наделать детей любому классу, а детям еще детей и так далее. И ветвистое генеалогическое дерево станет реальностью.
Идем дальше, пишем скрипт в новом FLA
Object.prototype.old_trace = trace // сохраним trace в old_trace
Object.prototype.trace = function() { // перезададим стандартный трейс в Object
old_trace("Object trace"); // вывод в output сообщения
};
Array.prototype.trace = function() { // перезададим стандартный трейс в Array
old_trace("Array trace"); // вывод в output сообщения
};
XML.prototype.trace = function() { // перезададим стандартный трейс в XML
old_trace("XML trace"); // вывод в output сообщения
};
this.myArray = [1, 2, 3]; // создадим массив
this.myXML = new XML ("<xml><item>Hello world</item></xml>") // создадим XML
// вызовем трейс из разных объектов:
this.myArray.trace(); // из массива
this.myXML.trace() // из XML
this.trace(); // из рута (он мувиклип, это я на всякий случай напоминаю)
Уффф...
1) Стандартные функции можно подменять. На самом деле они остаются на старом месте, но их можно нейтрализовать, поместив в прототип одноименную функцию.
2) Во всех случаях мы вызвали одну и ту же функцию, но результат разный. Причина тому - у каждого из объектов в его прототипе была задана своя функция и как только она обнаруживалась, то дальнейшие поиски прекращались.
Подробнее:
В нашем примере у объекта XML к моменту вызова функции trace в иерархии находится 3 функции с именем trace: одна в прототипе XML, другая в прототипе Object, третья в самом Object. Реально срабатывает только одна - та, которая ближе всего в иерархии.
Попробуйте удалить по очереди функции из скрипта, каждый раз тестируя результат.
Итак, мы вкратце рассмотрели некоторые, наиболее важные свойства прототипов. Иногда я сознательно упрощал обстоятельства дела, чтобы не забивать голову пока не нужным хламом.
Если всё еще интересно, и хочется узнать побольше, то настоятельно рекомендую http://www.debreuil.com/docs/, там есть линк на русскую версию, которая пока не работает. Я сейчас пойду в рассылку поругаюсь: еще месяц назад должны были восстановить.
(Прошел месяц, я сходил, поругался, не восстановили. А зип мозно взять здесь)
В крайнем случае, пишите мне на мыло, я вышлю зипом.
Применение
Это самое простое, во всей этой истории. Того, о чем я сегодня писал хватит на 90% случаев использования прототипов.
Основным их использованием является расширение количества функций.
Что я делаю, когда мне нужно например, разобрать XML по полочкам?
1) http://www.layer51.com/proto
2) захожу на раздел XMLnode
3) копирую свои же функции %)
4) делаю разбор XML, если забываю, как это делается, то захожу к себе на уроки и смотрю "XML - прогулка по дереву" и "Узел XML: применяем атрибуты к мувиклипу"
В реальной жизни бывает трудно найти подходящую прототипную функцию, но часто достаточно подсмотреть чужие приемы обращения с объектом, чтобы существенно продвинуться в решении своей задачи.
вот некоторые линки на библиотеки прототипов.
http://www.sephiroth.it/download/prototype.php
http://www.five100.com/protos/protoJunkie.html
я обычно дальше layer51 не хожу, там удобная навигация, нет никаких картинок всё работает очень шустро и понятно. Для того, чтобы использовать любую из находящихся там функций достаточно посмотреть пример использования, прочитать описание, комментарии, если есть, и все-таки советую потестить скопированную функцию. Свои следы там оставляют не только профессионалы %)
Для ознакомления, я полагаю, будет достаточно. пишите на dembicki@narod.ru, задавайте вопросы, предлагайте темы для статей.