Появление Netscape Navigator 3.0 и новой версии JavaScript 1.1 заставляет продолжить обзор возможностей управления сценариями просмотра Website, который был опубликован в предыдущем выпуске "Открытых Систем сегодня" (Computerworld Россия 46, 1996). В новой версии языка были введены: возможность взаимодействия JavaScript и Java, определение установленных модулей расширений (plug-in), новые типы объектов (Area, Function, Image) и ряд других особенностей, которые, по мнению разработчиков, должны повысить мощь программирования на JavaScript. По своей природе JavaScript - это объектно-ориентированный язык программирования. В отличие от других языков данного типа, JavaScript не оперирует абстрактными объектами. Все его конструкции определены для объектов Navigator. Именно с этих позиций и следует рассматривать развитие языка.
Объект навигатора - это либо конструкция HTML, чаще всего контейнер, либо рабочая область программы Netscape Navigator. Если брать конструкции HTML, то каждый контейнер имеет атрибуты, которые в JavaScript рассматриваются в качестве свойств объекта.
Над объектами могут быть выполнены некоторые функции, которые в терминах JavaScript называются методами. Помимо методов существуют и просто функции, которые могут быть определены программистом.
Свойства объектов могут быть переопределены в процессе выполнения программ, что позволяет построить гибкие сценарии просмотра гипертекстовых страниц Website или "оживить" страницы. Однако не все свойства переопределяются. Некоторые свойства могут быть только прочитаны скриптом, но не изменены в процессе его выполнения.
Развитие языка идет в направлении расширения типов объектов Navigator, с которыми может манипулировать программист, а также изменения свойств объектов, точнее, расширения возможностей их переопределения.
Массивы
Первым типом новых объектов, которые мы рассмотрим, являются массивы. Тип Array введен в JavaScript 1.1 для возможности манипулирования самыми разными объектами, которые может отображать Navigator. Это - список всех гипертекстовых ссылок данной страницы Website, список всех картинок на данной странице, список всех апплетов данной страницы, список всех элементов формы и т.п. Пользователь может создать и свой собственный массив, используя конструктор Array(). Делается это следующим образом:
new_array = new Array() new_array5 =
new Array(5) colors = new Array("red", "white", "blue")
Размерность массива может изменяться. Можно сначала определить массив, а потом присвоить одному из его элементов значение. Как только это произойдет, изменится и размерность массива: colors = new Array() colors[5] = "red". В данном случае массив будет состоять из 6 элементов, так как первым элементом массива считается элемент с индексом 0.
Для массивов определены три метода: join, reverse, sort. Join объединяет элементы массива в строку символов, в качестве аргумента в этом методе задается разделитель:
colors = new Array("red", "white", "blue") string =
acolors.join(" + ")
В результате выполнения присваивания значения строке символов string мы получим следующую строку: string = "red + white + blue". Другой метод, reverse, изменяет порядок элементов массива на обратный, а метод sort отсортировывает их в лексико-графическом порядке.
У массивов есть два свойства: length и prototype. Length определяет число элементов массива. Если нужно выполнить некоторую рутинную операцию над всеми элементами массива, то можно воспользоваться циклом типа:
color = new Array("red", "white", "blue") n = 0 while(n != colors.length) {... операторы тела цикла ...}
Свойство prototype позволяет добавить свойства к объектам массива. Однако чаще всего в программах на JavaScript используются встроенные массивы, в основном графические образы (Images) и гипертекстовые ссылки (Links).
Графика
До Navigator 3.0 в JavaScript были только встроенные объекты типа Image. В новой версии языка появился конструктор для этого типа объектов:
new_image = new Image() new_image = new Image(width, height)
Часто для создания мультипликации формируют массив графических объектов, которые потом прокручивают один за другим:
img_array = new Array() img_array[0] = new Image(50,100) img_array[1] = new Image(50,100) ... img_array[99] = new Image(50,100)
У объекта Image существует 10 свойств, из которых, на мой взгляд, самым важным является src. Так, для присваивания конкретных картинок элементам массива img_array следует воспользоваться следующей последовательностью команд:
img_array[0].src = "image1.gif" img_array[1].src = "image2.gif" ... img_array[99].src = "image100.gif"
В данном случае можно было воспользоваться и циклом для присвоения имен, так как они могут быть составлены из констант и значения индексной переменной.
В новой версии языка объект типа Image может быть поименован в HTML-теге IMG. После этого можно обращаться к нему по имени. Здесь следует учитывать, что если Image применяется внутри формы, то он является свойством этой формы. В одном случае для следующего графического объекта должны быть использованы разные составные имена:
document.kuku.car.src = "car1.gif"
В первом случае задан поименованный объект типа IMG, а во втором объект этого же типа, но внутри формы.
Однако наиболее часто в примерах использования скриптов можно встретить обращение к Image по индексу в массиве всех графических объектов данной страницы. Если наш объект, например, второй Image на странице, то будь он внутри формы или за ее пределами, к нему всегда можно обратиться по индексу: document.images[1].src = "car1.gif". Расширяя пример с массивом Image, построим теперь документ, в котором будет встроена мультипликация, определенная нашим массивом:
Довольно часто используют не мультипликацию, а выбор картинки через OPTION, другой новый объект JavaScript. При этом можно через поле формы SELECT менять не только саму картинку, но и гипертекстовую ссылку, которая может быть связана с ней. На гипертекстовую ссылку также можно выходить по индексу:
document.links[index].href = kuku.html
Данный прием оправдан и с точки зрения интерфейса навигатора. При применении такого рода массивов ссылок не требуется перечислять их, а также листать страницы в рабочей области навигатора - можно просто выбрать ссылку из "выпадающего" меню. Другой способ для сокращения числа нажатий на клавиши - использование событий. В том же объекте OPTION можно применять событие onChange, что делает необязательным нажатие кнопок типа submit. В этом случае достаточно будет просто выбрать альтернативу и перейти к новой странице сразу после выбора.
Стеки гипертекстовых ссылок
Не обошли своим вниманием авторы JavaScript и стеки гипертекстовых ссылок. В язык теперь введен новый тип объектов типа Area. Area - это элемент контейнера MAP, который определяет client-site imagemap. Собственно, главное достоинство такого объекта состоит в том, что гипертекстовые ссылки, которые определены в Area, стали доступны для переопределения. Они появляются в массиве обычных ссылок страницы, и можно как получить значение URL, так и переопределить его. К объекту Area нельзя обратиться по имени. Можно использовать только индекс массива гипертекстовых ссылок документа.
В контексте стека гипертекстовых ссылок интересно рассмотреть еще одну возможность JavaScript, связанную с переходом по гипертекстовой ссылке вообще. В обычном случае параметр HREF контейнера A должен иметь какое-нибудь значение. Если, например, по событию onClick необходимо открыть новое окно и в старом сохранить отображенный документ, то его URL следует указывать в качестве значения HREF. В противном случае, в старое окно будет загружена пустая страница, если HREF=" ". В новой версии JavaScript введена функция void. Точнее, тип void, который означает отсутствие какого-либо значения.
Если нам необходимо выполнить некоторые действия при выборе гипертекстовой ссылки, не перегружая при этом текущие страницы, то в параметре HREF можно указать конструкцию:
kuku
Таким приемом часто пользуются при программировании событий, связанных с проходом манипулятора (мыши) через поле гипертекстовой ссылки.
Фреймы и окна
При работе с фреймами и окнами в предыдущих версиях JavaScript постоянно приходилось отслеживать последовательность открытия окон и фреймов для того, чтобы аккуратно их потом закрывать. На некоторых неточностях работы с окнами были основаны так называемые mail-bombs. Суть этих "подарков" заключалась в том, что если пользователь по почте принимает документ, который состоит только из одной команды - window.close(), то система, не спрашивая пользователя, закрывала текущее окно, а в этот момент таким окном является окно электронной почты. Теперь перед тем, как что-либо закрыть, система будет спрашивать разрешения. Правда, опять-таки не всегда. Если в момент получения команды на закрытие окна на экране только одно окно Navigator, то система его закроет без каких-либо комментариев.
Однако изменения в работе с окнами и фреймами этим не исчерпываются. Во-первых, в систему введено новое свойство opener, которое определено для текущего окна или фрейма, а также методы blur и focus распространены на работу с окнами.
Свойство opener определяет окно документа, который вызвал открытие окна текущего документа. Свойство определено для любого окна и фрейма. Если необходимо выполнить некоторые функции по отношению к окну, открывшему данное окно, то можно использовать выражение типа: window.opener.[method]. Например, если требуется закрыть окно-предшественник, то можно просто выполнить метод close - window.opener.close().
Точно таким же способом можно изменить содержание этого окна при помощи методов write или writeln. Можно менять и другие свойства объектов в окне-предшественнике. Следующий пример взят из дополнений к спецификации JavaScript компании Netscape Communications:
window.opener.document. bgColor="cyan"
В данном случае для окна-предшественника определен светло-голубой цвет фона.
Но самое замечательное, что предшественника можно менять. Таким образом, автор получает возможность открывать и закрывать окна не в строго иерархической последовательности, а произвольно. Управление многооконным интерфейсом в этом случае становится более гибким:
window.opener= new_window window.opener = null
Первый пример переназначает для текущего окна окно-предшественник, в то время как второй вообще защищает предшественника от каких-либо действий. Все, что было сказано об окнах, распространяется и на фреймы, которые являются просто частным случаем окна.
Часто при работе с фреймами один фрейм может быть порожден путем разбиения другого на части. Если при этом потребуется обратиться к окну-предшественнику фрейма-предшественника, то свойство opener здесь незаменимо. В предыдущей статье было рассмотрено закрытие окна с фреймами путем загрузки в окно parent страницы терминатора. С появлением свойства opener необходимость в такого рода фокусах отпала.
Как видно из этого примера, разработчики языка следуют за пожеланиями авторов Web-узлов и практикой применения JavaScript.
Кроме обращения к различным свойствам окон и фреймов разработчики расширили действие методов blur и focus с фреймов до окон. Теперь не только фрейм, но и окно может быть сделано текущим при помощи метода focus или, наоборот, переведено в фон при помощи метода blur. В ряде случаев, например при порождении нескольких страниц, обращение к этим функциям бывает довольно полезным.
Наследование кода скриптов различными страницами
Отсутствие какого-либо наследования между различными страницами Web-узла заставляло разработчиков перетаскивать из одной страницы в другую довольно большое количество часто используемых функций и переменных. Разговоры о том, что было бы неплохо получить доступ к глобальным ресурсам или возможность определять такие глобальные ресурсы, ведутся с самого момента появления JavaScript. К сожалению, стройного логичного механизма передачи параметров, функций и переменных от одного окна или фрейма к другому нет и в JavaScript 1.1. Однако некоторые шаги в этом направлении сделаны.
У контейнера SCRIPT появился атрибут SRC. Это дает возможность авторам строить своеобразную библиотеку функций, к которым можно обращаться из любой страницы, где будет ссылка на такую библиотеку. При этом вовсе не обязательно размещать саму библиотеку на том же сервере, где находятся и гипертекстовые страницы узла. Можно использовать и чужие функции, написанные кем-либо из ветеранов программирования на JavaScript в любой точке земного шара. В атрибуте SRC применяется обычный URL.
Внутри файла скриптов не используются теги SCRIPT. Это обычный файл с использованием определений функций и переменных. Естественно, что применение чужих скриптов может обернуться не только полезными приобретениями, но и непредсказуемыми проблемами.
Для обычных пользователей страниц узла подкачка большого количества скриптов может стать просто дополнительным источником затрат. Скорее всего, понадобится пара функций из библиотеки на одной странице, а качать придется всю библиотеку.
Другой новой возможностью работы с функциями стало введение объекта Function. Объект Function порождается конструктором Function:
new_Function = new Function(arg1,arg2,...,argn, function_body)
Главное отличие от обычного декларирования функции заключается в том, что в данном случае порождена переменная new_Function, с которой можно работать, как с любым другим объектом. При обычном переопределении функции такой переменной не порождается.
Как любой объект, Function имеет свои свойства, но не имеет методов. В качестве свойств функции выступают аргументы и возможность назначения новых свойств через prototype.
В заключение разговора о функциях и наследовании хочется еще раз обратить внимание на свойство opener окон и фреймов. Его можно использовать при обращении к объектам страницы-родителя, что позволяет компенсировать отсутствие наследования и глобальных переменных в JavaScript.
Java, JavaScript и включаемые модули
В новой версии языка есть возможность организовать взаимодействие между Java-апплетами и скриптами JavaScript. Достигается это за счет применения атрибута MAYSCRIPT в контейнере APPLET. Собственно, в JavaScript определен объект типа APPLET, к которому можно обращаться либо по имени, либо по индексу в массиве апплетов. У этого объекта есть только одно свойство - имя. Никакие другие свойства или методы для данного типа объектов не определены. Сами детали взаимодействия апплетов и скриптов лучше всего обсуждать в рамках программирования Java-апплетов, поэтому в данной статье мы эти особенности опустим.
Кроме апплетов JavaScript позволяет работать и с включаемыми модулями. Последние представляют собой массив соответствующего типа, для которого определен ряд свойств. Используя эти свойства, можно определить установленные включаемые модули и их соответствия MIME-типам. Назначать включаемые модули или манипулировать ими нельзя.
Что дальше?
Следуя логике авторов JavaScript и направлениям развития World Wide Web, следует ожидать появления новых типов объектов и изменения свойств существующих. Кроме того, видимо, появятся глобальные переменные и функции. Порождение абстрактных типов тоже не за горами, хотя не совсем понятно, кто выиграет от такой новации. Постепенно совершенствуется и поддержка встроенных функций на разных платформах, так, например, функция random() реализована в настоящее время везде, что не исключает использования и своих собственных датчиков случайных чисел.
И еще, скорее всего, следует ожидать компилятора JavaScript для клиента. Будет ли данная возможность встроена в Navigator или это будет отдельный модуль, пока не ясно, но появление библиотек функций - движение в этом направлении, которое хорошо согласуется с принципами кэширования гипертекстовых страниц.
Павел Храмцов - рук. группы РНЦ "Курчатовский Институт". С ним можно связаться по тел.: 196-91-24 или электронной почте: paul@kiae.su