Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
Форум программистов > JavaScript: Общие вопросы > Обьекты, прототипы, эффективные интерфейсы |
Автор: Sardar 30.4.2005, 23:30 |
Предисловие. Началось всё с http://forum.vingrad.ru/index.php?showtopic=49934. А что такое прототипы? Многие пытаються увидеть в JS обьектно ориентированный язык, но на самом деле это не так. JavaScript это обьектно протитипный язык програмирования, имеющий свои особенности мышления. Конечно можно за уши притянуть ООП подход к JS, но мы увидим что подобные громоздкие обороты лишни и к счастью не используються. Просто представь себе кучу классов, типов в JS, не динамичное поведение обьектов, жёстко прописанные интерфейсы и прочее... Впрочем почти всё, что здесь будет изложено, не используется, либо используется не явно, т.к. большинство народу просто не знают(догоняют) понятия динамически изменяемых и эффективных интерфейсов ![]() JS простой язык, идеально подходящий для манипулирования существующими обьектами. Такими обьектами могут быть элементы страницы, GUI(XUL) и т.п. Смысл этого текста научиться думать в обьектно прототипном стиле. Инфы много, я буду часто повторяться дабы вложить и закрепить инфу. Главы буду разбивать по постам. Любое утверждение будет подтверждаться примером. В этом топе запрещаеться писать отзывы и т.п. Создам для этого одтельный топ ![]() Там же можно направлять меня в те темы, которые не ясны, я их буду чётче/шире раскрывать. nJoy ![]() |
Автор: Sardar 30.4.2005, 23:40 | ||
Начнём с основ, постепенно углублясь в детали. Всё есть обьекты. В JS всё есть обьекты. Примитивные типы тоже существуют, для улучшения производительности и не только, но они при случае конвертируются в обьекты. Узнать тип переменной можно ключевым словом typeof.
Усвоив что всё есть обьекты, мы придём к первому правилу: все обьекты имеют динамически изменяемый интерфейс(читай ниже) Исключение: у стандартных встроенных в браузер обьектов нельзя подменить методы, т.е. у подобных обьектов(массивы, HTML элементы и т.п) есть свойствo "только для чтения", которое мы, как программисты использовать в наших обьектах не можем. Это всё нативные методы/свойства, выполняющие работу на "низком" уровне. На заметку: у обьектов есть только свойства ![]() На заметку: во многих языках есть понятие полей обьекта, при изменении которого вызывается функция обработчик(т.н. get/set функции). Изначально нетскейповцы придумали в стандартном интерфейсе Object(а следовательно у всех) методы watch/unwatch, следящие за обращениями к свойству обьекта и вызывающие функции обработчики. Под ИЕ это дело не пошло ![]() Мозилла "не оффициально" поддерживает __defineGetter__|__defineSetter__. Пример можно увидеть http://webfx.eae.net/dhtml/ieemu/eventobject.html ИМXО хорошая и удобная вещь для создания свойств только для чтения. К сожалению сейчас пока нет универсального единого способа сделать свойства обьекта "только для чтения". Впервые достала фича форума, если время между постами меньше 5 минут, посты сливаються. Мешает... |
Автор: Sardar 1.5.2005, 00:15 | ||||||
Эффективный интерфейс обьекта. Любой обьект имеет определённый интерфейс - список всех свойств обьекта(выше мы обусловились что методы это свойства/поля обьекта ![]() ![]() Теперь поговорим от том как же формируется интерфейс обьекта. Рекомендуется выспаться, пробежать пару км. для бодрости, выпить кофе ![]() Определение вложенных функций и наследование контекста. Если в теле одной функции определить вторую функцию, то все переменные доступные в контексте первой функции доступны и в контексте вложенной. Это очень удобно, например для создания различных обработчиков для обьектов, нам не нужно создавать какие то внешние регистры в которых придётся запоминать ссылки на обьекты. Исключение: ссылка this и arguments являются личными для каждой функции и не наследуются по контексту.
Замечаем что переменные lnkи parg также доступны и ниже по контексту в новой функции. Также видим что this и arguments у каждой функции свои, для имортирования их ниже по контексту, нужно создать на них ссылки с другими именами. При вызове test как конструктора, в this храниться ссылка на только что созданный обьект. При вызове a() мы вызываем функцию в глобальном контексте, т.е. this в a указывает на window. Поэтому alert(this.field) выдаст undefined. При вызове test как простую функцию this указывет на window, т.е. мы создаём новую переменную в глобальном контексте с именем field. Как и ранее a вызывается в глобальном контексте, потому field нам доступна. Важно: из теста видно что мы наследуем контекст, т.е. все переменные с их текущими значениями. При следующем вызове эти переменные поменяют свои значения, но тем не менее в созданной функции они продолжают содержать "старые" значения. На самом деле при каждом вызове test создаётся новая функция a! Которая и содержит актуальные значения. Убедитсься мы в этом можем следующим кодом:
Может показаться не эффективным вот так транжирить память, но на самом деле всё работает правильно при правильном проектировании программы ![]() На заметку: на самом деле в хорошем интерпретаторе(а плохие быстро умирают ![]() Механизм похож на вложенные анонимные классы в Java. Теперь более реальные примеры, создание "делегатов", например для вставки их как параметры у window.setTimeout. Напомню что set(Timout|Interval) работает только с обьектом функцией. Делегат в нашем случае это функция содержащая сылку на обьект и метод, который вызывается при её вызове.
Как видим сохранение контекстов в создаваемых функциях очень удобно, когда нужно определять каких то уникальных обработчиков для обьектов. Например для создания обработчиков событий для элементов страницы. И так в этой точке должно быть ясно что:
|
Автор: Sardar 1.5.2005, 00:36 | ||||
Задание интерфейса обьекта в функции(конструкторе). Теперь мы знаем что есть обьекты и что есть функции и контексты. Рассмотрим такой код:
Если в этот момент уже потерял способность думать, прими душь, потом продолжим ![]() Представим что мы интерпретатор, по шагам выполним код(опустим определение функции test):
Отсюда понятно почему мы извне не можем обратиться к локальным переменным конструктора, а все созданные внутри него функции доступ до них имеют(имеют ссылку на его контекст, бывший текущим в момент их создания). Важно: повторю все созданные внутри конструктора функции это самостоятельные обьекты, т.е каждый новый обьект от test имеет свои собственные функции-методы, которые содержат одинаковый код. Это в корне отличается от других ООП языков, где обьекты являються экземплярами класса и все имеют один и тот же код в методах(код храниться в классе). В JS каждый обьект с методом созданным как показано выше получает свой собственный обьект-метод(код храниться в обьекте). Что бы было еще более ясно о локальных переменных конструктора(на них мы потом будет делать "приватные" поля обьекта), дополним предидущий код:
Как видим созданный method2 уже не имеет доступа к локальным переменным конструктора. Для справки: все созданные свойства обьекта являются его личными(сохраняются в его памяти, другой обьект может их не иметь). Метод method2 мы добавили уже после того как конструктор отработал, это тоже личное свойство обьекта. Нет никакой разницы между определением свойства в конструкторе и вне конструктора, используеться один и тот же механизм динамически создаваемых свойств обьектов. Различии появяться когда мы приступим к прототипам ![]() Предлагаю сделать паузу ![]() ![]() |
Автор: Aliance 1.5.2005, 20:25 |
Обсуждение ведем http://forum.vingrad.ru/index.php?showtopic=50739 |