На этот раз Вашему вниманию предлагается
сделанный мной перевод новой статьи Andrew Clinick
Scripting Events,
посвященной реализации обработчиков событий в Internet Explorer,
Windows Script Host и Windows Script Components.
Обработчики событий в сценариях. Это должно быть очень легко. В конце концов, многие приложения, как от Microsoft, так и от других разработчиков, позволяют Вам писать код сценария, который реагирует на события, сгенерированные объектами приложения. Это хороший способ писать сценарии, поскольку приложение вызывает Ваш код в нужное время, и все, что Вам (разработчику) нужно сделать - это написать код обработки событий. По крайней мере, в теории. В реальности, события обрабатываются и реализуются приложениями множествами способов, и очень скоро может стать непонятным, каким способом лучше писать обработчики событий в сценариях.
Я воспользуюсь случаем, чтобы развеять часть путаницы, объяснив различные возможности для написания обработчиков событий в сценариях популярных приложений - в частности, Internet Explorer, Windows Script Host и Windows Script Components.
Как работают события в обработчике сценариев?
Прежде чем углубиться в детали того, как различные приложения обходятся с событиями, давайте взглянем на то, как сам обработчик сценариев (компонент, который на самом деле выполняет код сценариев в приложении) работает с событиями. По существу, разработчикам приложений доступны два механизма обработки событий: встроенная, или "автомагическая" поддержка, и вызов функций/процедур.
"Автомагическая" поддержка
Оба обработчика сценариев Microsoft Windows, JScript и Visual Basic Scripting Edition (он же VBScript), предоставляют встроенный механизм перехвата событий сценарием через соглашение об именах. Если Вы программируете на VBScript, то Вы наверное уже знакомы с соглашением об именах ИмяОбъекта_ИмяСобытия для обработчиков событий в Visual Basic и в VBScript. Если приложение предоставляет обработчику сценариев объект, и включает обработчик сценариев, то любая процедура, имя которой удовлетворяет соглашению об именах, будет вызвана при генерации события. Например, если сценарий содержит объект "order", который может генерировать событие "onNew", то метод-обработчик должен быть назван order_onNew(). Чтобы "автомагический" перехват событий работал, объект должен быть доступен обработчику сценариев при компиляции сценария.
К счастью, это не является новостью для программистов на VBScript, но нам часто задают вопрос: почему JScript не поддерживает этот механизм. JScript поддерживает встроенный механизм перехвата событий сценарием через соглашение об именах, но, к сожалению, это малоизвестно. Когда мы разрабатывали JScript 3, в команде разработчиков шли большие дискуссии о том, как наилучшим образом добавить эту поддержку в JScript. Должна ли она следовать нотации VBScript, или должна быть реализована как-то по-другому? Было ощущение, что соглашение ИмяОбъекта_ИмяСобытия находится не в духе JScript. Более тог, мы осознали, что уже может существовать код, использующий соглашение об именах, поэтому добавление его в версию 3 могло привести к серьезным проблемам совместимости.
Альтернативным предложением была нотация ИмяОбъекта::ИмяСобытия, поскольку JScript относится к C-подобным языкам (и нет, он не имеет никакого отношения к Java - это второй по популярности вопрос, после вопроса об обработке событий в JScript!). Если воспользоваться примером с объектом order снова, то обработчик JScript для события onNew будет выглядеть как order::onNew. Чтобы проиллюстрировать это, я написал простой сервер сценариев с помощью Visual Basic и Windows Script Control, с помощью которого Вы сможете испытать обработчики событий. Приложение предоставляет объект order, который имеет 3 события - onNew, onSave и onDelete, а также простой текстовый редактор, позволяющий писать код сценариев.
Вызов функций/процедур
Когда приложение использует обработчик сценариев (примерами таких приложений могут быть Internet Explorer, IIS и Windows Script Host), то оно может получить доступ ко всем функциям и процедурам, написанным в Вашем сценарии. Приложение может не только видеть эти функции, но и вызывать их. Эта возможность прямого вызова кода позволяет приложениям предоставить обработчику сценариев альтернативный механизм перехвата событий. Вместо того, чтобы использовать встроенную возможность реагирования на события, приложение может вызывать для обработки событий объекта соответствующие функции сценария. Этот механизм позволяет приложениям определять другой синтаксис для обработчиков событий, который, будучи мощным, может вызвать непонимание того, как реагировать на события в сценарии. Приложение может использовать различные соглашения об именах и кодировании. Например, в каком-либо приложении событие onNew объекта order может иметь в сценарии функцию-обработчик с именем orderOnNew.
События в Internet Explorer
Internet Explorer является основным источником неразберихи в отношении событийно-управляемых сценариев, в основном потому, что имеет много различных путей для перехвата событий. Напрашивается вопрос, зачем необходимо иметь столько разных путей для достижения одной цели? Главной причиной является то, что механизм событий в HTML не был по-настоящему стандартизован, и поэтому IE вынужден поддерживать несколько способов перехвата событий в HTML, а также обеспечить работу с функциональностью, отсутствующей в других браузерах (например, ActiveX). Это замечательно для конечного пользователя, поскольку IE может отображать практически любые страницы, но это же делает жизнь разработчика сценариев более сложной.
Встроенные обработчики событий
Встроенные обработчики событий являются самым широко используемым механизмом обработки событий в IE, поскольку поддерживается как IE, так и Nescape. Основным принципом является то, что Вы вставляете сценарий внутрь элемента HTML, а браузер делает перехват событий за Вас. Например:
Click me inline - JScript
Код, помещенный а атрибут onclick, будет вызван когда пользователь щелкнет мышкой тексту, окруженному элементами <span>. Этот механизм хорошо подходит для небольших объемов кода, но слишком громоздок, если Вам нужно вставить большой сценарий. Этот механизм работает как в VBScript, так и в JScript.
За кулисами Internet Explorer передает обработчику сценариев код и просит создать анонимную функцию (то есть функцию без имени). Те из Вас, кому знаком VBScript, наверное удивляются, как это сделано, ведь VBScript не поддерживает анонимные функции. На самом деле, VBScript создает метод с именем "anonymous", содержащий переденный код, и возвращает указатель на функцию, которая будет перехватывать события.
Указатели на функции
Любая функция в JScript является объектом, что позволяет передавать указатели на функции и использовать их для вызова функций. Это обеспечивает гибкость при написании обработчиков событий, позволяя использовать одну функцию для обработки одного или нескольких событий. Чтобы обрабатывать события с помощью указателей на функции, просто присвойте свойству ИмяСобытия объекта указатель на функцию. В JScript это сделать очень просто. Например:
window.onclick=foo
function foo()
{
alert('you clicked on the window')
}
В первых версиях VBScript не было способа использовать указатели на функции, поскольку если Вы присвоете свойству window.onclick функцию foo, то VBScript вызовет foo, а не даст указатель на функцию. В VBScript 5.0 был введен метод GetRef. Он создает ссылку на функцию, позволяя установить обработчик события. Например:
set window.onclick=GetRef("foo")
function foo()
alert('you clicked on the window')
end function
Обработчики событий в блоке <script>
Все использованные до этого обработчики событий хорошо работали для событий HTML, но не предоставляют возможности написать обработчик событий от объектов ActiveX, которые Вы можете иметь на своей странице. Чтобы обеспечить такую поддержку, Internet Explorer предоставляет расширение блока <script>, которое позволяет задать, для какого объекта и какого события должен исполняться этот сценарий. Например:
Click me script block handler - JScript
Соглашение об именах
Еще один, специфичный для Internet Explorer, механизм перехвата событий заключается в именовании Ваших функций таким образом, чтобы они включали имя объекта и имя события. Если Ваша функция называется ИмяОбъекта.ИмяСобытия, IE будет вызывать эту функцию автоматически. Например:
Click me naming - JScriptПримечание: этот механизм работает только для JScript.
Автомагика
В дополнение ко всем специфичным для IE механизмам обработки событий, Вы можете также использовать встроенный "автомагический" перехват событий, обеспечиваемый обработчиками сценариев. Ключем к использованию этого механизма является понимание того, когда происходит начало перехвата событий. Если объект или элемент HTML доступны при загрузке страницы, тогда обработчик сценариев будет выполнять перехват событий. Если, однако, объект или элемент были добавлены после загрузки страницы (например, присвоением свойству innerHTML), то обработчик сценариев не будет выполнять перехват событий. Хорошим в этом механизме является то, что он позволяет реагировать на события ActiveX.
Click me Built In - JScript Click me Built In - VBScriptПроверьте в работе эти примеры обработчиков событий.
События в WSH
Windows Script Host имеет гораздо более простой механизм перехвата событий нежели Internet Explorer, так как предоставляет для этого только два пути. (Эй, два лучше чем пять!) Тем не менее, написание в WSH кода, работающего с событиями, все еще может представлять собой сложную задачу, не последняя причина чему - необходимость быть уверенным, что сценарий все еще исполняется, когда возбуждается событие. Это является проблемой, поскольку сценарии WSH завершают работу, как только выполняется последняя строчка кода, что может произойти раньше, чем возбудится обрабатываемое Вами событие. Чтобы решить эту задачу, я предлагаю использовать метод WScript.Sleep (более подробно об этом см. в http://msdn.microsoft.com/scripting/windowshost/doc/wsMthSleep.htm) и создать цикл, который засыпает на длительное время. При возбуждении события, используйте метод WScript.Quit для завершения работы сценария. (Мы понимаем, что это делать непросто; решение этой проблемы находится в списке улучшений к следующей после 5.6 версии WSH.) Когда Вы решите, как обеспечить вызов Вашего обработчика событий, Вам будет необходимо выбрать между двумя механизмами обработки событий.
WSH поддерживает как "автомагический", так и основанный на соглашении об именах (naming-convention-based) перехват событий. Система соглашении об именах, анонсированная в WSH 1.0, обеспечивает возможность реагировать на события от объектов, созданных с помощью WScript.CreateObject. В то время это было необходимо, поскольку не было другого пути создавать внешние объекты в WSH. WScript.CreateObject похож на CreateObject из VBScript, но имеет дополнительный второй параметр, который задает соглашение об именах для функций обработки событий. Например:
Set myobject = WScript.CreateObject("someobjectwithevents", "myobject_")
В этом случае, WSH создаст экземпляр объекта Someobjectwithevents, и любая функция, начинающаяся с myobject_ и заканчивающаяся именем события, будет вызвана при возбуждении события. В WSH это достигается внутренней обработкой события и вызовом функции, которая удовлетворяет соглашению об именах. В VBScript это обычно не имеет большого удивления, поскольку синтакис "автомагического" перехвата событий выглядит как ИмяОбъекта_ИмяСобытия. В JScript, однако, это немного странно, поскольку ИмяОбъекта::ИмяСобытия работать не будет. Вот пример для перехвата событий с помощью CreateObject:
set myorder2 = WScript.CreateObject("ordersystem.clsorder","myorder2_")
myorder2.neworder(1)
sub myorder2_onNew()
Wscript.echo "new order received from myorder2"
end sub
Команда разработчиков Windows Script осознавала, что механизм WScript.CreateObject может сбить с толку, и в WSH 2.0 в составе файла .wsf был представлен эленент <object>. Элемент <object> в WSH работает почти так же, как и элемент <object> в HTML, за одним исключением: по умолчанию, сгенерированные элементом <object> события не будут перехвачены сценарием. Чтобы разрешить перехват событий, необходимо установить в True атрибут Events элемента <object>. Например:
WSH включает в себя еще одну функцию для перехвата событий от объектов, которые были созданы после того, как сценарий был загружен. Это полезно для объектов, созданных через CreateObject (в WScript) или ActiveXObject в JScript. В объекте WScript существует метод ConnectObject для начала перехвата событий и метод ConnectObject для окончания перехвата событий.
set myorder3 = createobject("ordersystem.clsorder")
wscript.connectobject myorder3,"myorder3_"
myorder3.neworder(1)
sub myorder3_onNew()
Wscript.echo "new order received from myorder3"
end sub
Windows Script Components
Файлы Windows Script Components .wsc обрабатываются тем же компонентом, что и файлы .wsf, элементы <object> предоставляют ту же функциональность, в том числе и атрибут Events. Однако, методы The Wscript.CreateObject и Wscript.ConnectObject уникальны для WSH, и поэтому не могут быть использованы вне WSH.
Резюме
Использование событийно-управляемого программирования может сделать разработку сценариев значительно проще, если Вам знакомы способы привязки к событиям. Как я показал, выбор подходящего метода для написания обработчиков событий может оказаться достаточно сложной задачей. Выгодно оценить сравнительные достоинства разных подходов и выбрать тот, который отвечает Вашим требованиям и не усложняет чрезмерно разработку сценария. Как всегда, мы ждем Ваших отзывов и идей, как сделать разработку сценариев проще. Пожалуйста, пишите нам по адресу [email protected] и участвуйте в телеконференциях msnews.microsoft.com.