Роботландский Университет © А.А.Дуванов |
Однажды Иван Сидоров задумал написать приложение для просмотра своих любимых картинок. Он решил сделать меню и окошко для демонстрации в нем графики. Пользователь выбирает нужную позицию, и картинка появляется на экране.
Удобная организация данных в любой программе, это половина успеха! справедливо заметил Иван. С каждой строкой меню должны быть связаны две величины: название картинки (например, "Волк") и путь к ней на диске (например, "./pic/wolf.gif").
Названия нужно выводить на экран, а путь к соответствующему файлу присваивать свойству src объекта, построенного браузером для тега IMG.
Для задания одной строки своего меню, Иван построил такой объект:
// Объект для описания одной строки меню. // Конструктор объекта item. // Аргументы конструктора: // n - название картинки; // f - путь к картинке. function item(n, f) { this.name=n; // Название картинки. this.file=f; // Путь к картинке. } |
Как видите, описание конструктора объекта очень похоже на описание обычной функции! О том, что это конструктор, говорит ключевое слово this, отделяемое точкой от переменных name и file.
Если записать так:
function fun(n, f) { var name = n; var file = f; } |
то получится обычная функция, а не конструктор объекта, причем, функция совершенно бесполезная. Ведь переменные name и file существуют только внутри функции и не сохраняют свои значения после завершения ее работы.
Указания this сообщают браузеру, что построен объект, значит, от него можно образовывать экземпляры и внутри каждого экземпляра работать со своими переменными name и file, которые теперь уже называются не переменными, а свойствами объекта item.
// Создали первый экземпляр объекта item. var obj1 = new item("Волк", "./pic/wolf.gif"); // Создали второй экземпляр объекта item. var obj2 = new item("Коза", "./pic/goat.gif"); // Создали третий экземпляр объекта item. var obj3 = new item("Капуста", "./pic/cabbage.gif"); // Создали четвертый экземпляр объекта item. var obj4 = new item("Лодка", "./pic/boat.gif"); // Создали массив из построенных экземпляров: var items = new Array(obj1, obj2, obj3, obj4); |
Созданный массив items очень удобно использовать. Можно программным путем (при помощи метода document.write()) построить меню выбора на теге SELECT:
Созданный массив items очень удобно использовать. Можно построить меню выбора на теге SELECT:
<SCRIPT> language=JavaScript> <!-- // Построение меню document.write("<SELECT size="+items.length+">"); var ind; for(ind=0; ind<items.length; ind++) document.write("<OPTION>"+ items[ind].name+"</OPTION>"); document.write("</SELECT>"); //--> </SCRIPT> |
Первая команда
document.write("<SELECT size="+items.length+">");
строит тег SELECT, в котором атрибут size (число видимых строк меню) получает значение items.length, то есть он становится равным числу элементов в массиве items все строки меню будут видны на экране.
Позиции меню
<OPTION>...<OPTION>
строятся при помощи цикла:
var ind; for(ind=0; ind<items.length; ind++) document.write("<OPTION>"+ items[ind].name+"</OPTION>"); |
В позицию меню на экране с номером ind записывается значение
items | | массив экземпляров объектов item |
items[ind] | | экземпляр объекта item с номером ind в массиве items |
| свойство name экземпляра объекта item с номером ind в массиве items |
В нашем случае:
items[0].name равно "Волк"
items[1].name равно "Коза"
items[2].name равно "Капуста"
items[3].name равно "Лодка"
Последняя команда
document.write("</SELECT>");
строит закрывающий тег SELECT.
Таким образом, приведенный выше скрипт равнозначен следующему:
Зачем программировать этот простой код? Почему бы просто не записать его на HTML?
На самом деле, смысла в проделанной работе гораздо больше, чем кажется на первый взгляд: мы сделали построение HTML-кода зависимым от набора данных в массиве items. Это означает, что для нового меню изменится только items, но не код, который строит меню. Мы выделили в задаче подвижную часть набор данных, а код построения отлили в скрипте.
Страничка-иллюстратор, построенная Сидоровым, задается следующим кодом:
Для этой странички генерируется следующий HTML-код (с точностью до пробелов и концов строк):
Функция Make() срабатывает, когда пользователь выбирает новую позицию в меню. Сначала определяется индекс отмеченной позиции:
var ind = document.menu.sel.selectedIndex;
Затем на экран выводится соответствующее изображение:
document.images.pic.src=items[ind].file;
Для доступа к свойству src объекта IMG используется коллекция images (она входит в состав объекта document и содержит ссылки на объекты, построенные для всех тегов <IMG> документа).
Пользуясь преимуществом разделения данных и кода, Иван легко сделал новую страничку с другими картинками.
Что изменил Сидоров в старом файле? Очень немногое. Прежнее описание массива items заменилось новым:
var items = new Array( new item("Кораблик", "./pic/ship.gif"), new item("Грузовик", "./pic/truck.gif"), new item("Слон", "./pic/ elephant.gif"), new item("Компьютер","./pic/ibm.gif"), new item("Бабочка", "./pic/butterfl.gif") ); |
Еще Ивану пришлось изменить код построения атрибутов, задающих размер картинки. Команда:
document.write("<IMG name=pic src="+items[0].file+ " width=90 height=70 border=1 alt=''>");
заменилась на
document.write("<IMG name=pic src="+items[0].file+ " width=360 height=250 border=1 alt=''>");
Эти изменения Ивана расстроили. Нарушилась его задумка неприкосновенности кода построения страницы.
Однако, Иван легко вышел из положения, определив наряду с массивом items дополнительные переменные width и height. Конечно, пришлось немного переделать программный код. Данные для меню стали определяться так:
var width = 360; var height = 250; var items = new Array( new item("Кораблик", "./pic/ship.gif"), new item("Грузовик", "./pic/truck.gif"), new item("Слон", "./pic/elephant.gif"), new item("Компьютер","./pic/ibm.gif"), new item("Бабочка", "./pic/butterfl.gif") ); |
На код построения страницы теперь наложено табу! Читатель может вслед за Иваном изменить программу иллюстратора так, чтобы она учитывала значения переменных width и height.
На кухне Сидорова сегодня случилось много интересного и поучительного. Главное блюдо это собственный объект item, испеченный на JavaScript. Подведем кулинарные итоги.
Размышляя над построенной программой, дотошный Иван отметил в ней следующий недостаток.
Построить на одной странице несколько меню не просто. Ведь программа работает с глобальными переменными items, width, height и mark. Для нового меню придется ввести новые переменные с другими именами, например, items1, width1, height1 и mark1.
Кроме того, имена тегов FORM, SELECT, IMG тоже нужно менять с menu, sel и pic на menu1, sel1 и pic1. Для нового меню придется копировать все скрипты и вносить соответствующие изменения в именах.
Вся эта возня и рутина совсем испортили Ивану настроение.
Наконец, он придумал красивый выход: надо сделать меню при помощи объекта! Тогда от этого объекта можно будет образовать сколько угодно экземпляров, и никаких конфликтов с именами не будет.
Не откладывая дело в долгий ящик, Иван записал для этого объекта такой интерфейс:
// Конструируем первое меню. var menu1 = new Menu("menu1", 90, 70, "Волк", "./pic/wolf.gif", "Коза", "./pic/goat.gif", "Капуста","./pic/cabbage.gif", "Лодка", "./pic/boat.gif"); // Показываем меню на экране. menu1.Show(); // Конструируем второе меню. var menu2 = new Menu("menu2", 360, 250, "Кораблик", "./pic/ship.gif", "Грузовик", "./pic/truck.gif", "Слон", "./pic/elephant.gif", "Компьютер","./pic/ibm.gif", "Бабочка", "./pic/butterfl.gif"); // Показываем меню на экране. menu2.Show(); |
Как видите, Иван назвал новый объект Menu. В приведенном коде создаются два экземпляра этого объекта с именами menu1 и menu2. Каждый экземпляр выводит меню на экран при помощи метода Show(), который, конечно, должен быть включен в описание объекта.
Вызов конструктора объекта Menu у Ивана получился таким:
Menu(имя_экземпляра, ширина_картинки, высота_картинки, имя1, файл1, .... имяN, файлN);
Среди аргументов конструктора не очень понятно назначение первого: имя_экземпляра. Иван объяснил присутствие этого аргумента так.
Функция Make() должна работать тогда, когда пользователь меняет отметку в меню. То есть вызов этой функции должен быть расположен в теге SELECT:
<SELECT ... onchange='Make()'>
Но для экземпляра menu1 вызов должен быть записан как
Получается, что для построения HTML-кода нужно знать имя экземпляра объекта Menu. Кроме того, имя экземпляра, передаваемое конструктору как первый его аргумент, можно будет использовать для построения уникальных имен тегов FORM, SELECT и IMG, принимающих участие в построении экранного образа.
Программу с объектом Menu Иван поместил в отдельный файл
//--- Файл menu.js ------------------------- // Меню выбора на теге SELECT: // демонстрация картинок одного размера // ----------------------------------------- // Использование // ------------- // Меню представляет собой объект. Необходимо: // 1. Создать экземпляр объекта // var имя_экземпляра = new Menu(имя_экземпляра, ширина, высота, // название1, картинка1, // ...); // Здесь: // имя_экземпляра - имя созданного экземпляра; // ширина - ширина картинок; // высота - высота картинок; // название1 - текст для первой строки меню; // картинка1 - путь к файлу с первой картинкой. // 2. Настроить внешний вид меню, используя свойства объекта: // имя_экземпляра.bgcolor = цвет фона подложки меню // имя_экземпляра.border = толщина рамки вокруг картинки // имя_экземпляра.mark = номер отмеченной строки // (нумерация с 0) // 3. Показать меню на экране // имя_экземпляра.Show(); //------------------------------------------ // Описание одной строки меню (название, файл). function item(name, file) { this.name=name; // Название картинки. this.file=file; // Путь к картинке. } //------------------------------------------ // Конструктор главного объекта. function Menu(name, width, height) { // Свойства меню для пользователя. this.bgcolor = "white"; // Цвет фона подложки меню. this.border = 0; // Толщина рамки вокруг картинки. this.mark = 0; // Отмеченная строка в меню. // Методы меню для пользователя. this.Show = _Show; // Вывести меню на экран. // "Внутренние" свойства. this.items = new Array(); // Массив строк меню. this.name = name; // Имя экземпляра объекта. this.width = width; // Ширина картинки. this.height= height; // Высота картинки. // "Внутренние" функции. this.Make = _Make; // Обработка щелчка на строке меню. var len = arguments.length; // Число аргументов конструктора if(!len%2) len--; // должно быть нечетным. // Заполним массив items (строки меню). var j = 0; // Это индекс по массиву items. for(var i=3; i<len; i+=2) this.items[j++] = new item(arguments[i],arguments[i+1]); } //------------------------------------------ // Обработка изменения отметки выбора в меню. function _Make() { // Вычислить индекс отмеченной строки. var ind = document[this.name+"Form"][this.name+"Select"].selectedIndex; // Если отмечена новая строка, показать новую картинку. if(this.mark != ind) { document.images[this.name+"Img"].src=this.items[ind].file; this.mark = ind; } } //------------------------------------------ // Генерация HTML-кода для показа меню на экране. function _Show() { var str="<FORM name="+this.name+"Form>"+ "<TABLE border=1 bgcolor="+this.bgcolor+ " cellspacing=0 cellpadding=10>"+ "<TR><TD>"+ "Ваш выбор:<BR>"+ "<SELECT name="+this.name+"Select"+ " onchange='"+this.name+".Make()'"+ " size="+this.items.length+">"; for(var ind=0; ind<this.items.length; ind++) str += "<OPTION"+(ind==this.mark ? " selected":"")+ ">"+ this.items[ind].name+"</OPTION>"; str += "</SELECT>"; str +="</TD><TD>"; str +="<IMG name="+this.name+"Img"+ " src="+this.items[this.mark].file+ " width="+this.width+" height="+this.height+ " border="+this.border+" alt=''>"; str +="</TD></TR></TABLE></FORM>"; document.write(str); this.Make(); } |
Замечание. Вызовthis.Make() приводит в соответствие отмеченный пункт меню и картинку при перезагрузке содержимого окна (кнопка Обновить браузера) и переходах Вперед/Назад, так как в этом случае элементы формы сохраняют значения, установленные пользователем, а это может привести к неверному отображению построенного иллюстратора.
Для проверки работы объекта Menu Иван сконструировал страничку, на которой построил сразу четыре экземпляра объекта.
Конструктор объекта задается следующим кодом:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
function Menu(name, width, height) { // Свойства меню для пользователя. this.bgcolor = "white"; // Цвет фона подложки меню. this.border = 0; // Толщина рамки вокруг картинки. this.mark = 0; // Отмеченная строка в меню. // Методы меню для пользователя. this.Show = _Show; // Вывести меню на экран. // "Внутренние" свойства. this.items = new Array(); // Массив строк меню. this.name = name; // Имя экземпляра объекта. this.width = width; // Ширина картинки. this.height= height; // Высота картинки. // "Внутренние" функции. this.Make = _Make; // Обработка щелчка на строке меню. var len = arguments.length; // Число аргументов if(!len%2) len--; // должно быть нечетным. // Заполним массив items (строки меню). var j = 0; // Это индекс по массиву items. for(var i=3; i<len; i+=2) this.items[j++] = new item(arguments[i],arguments[i+1]); } |
В строках 04-06 свойства bgcolor, border, mark определены в расчете на доступ к ним извне. По умолчанию цвет фона меню сделан белым, рамка вокруг картинки отсутствует, в меню отмечена первая строка (ее номер 0).
В строке 11 определен массив items. В дальнейшем он заполняется объектами item, каждый из которых описывает пару (название, файл) для одной строки меню.
Заметьте, что в описании конструктора (строка 01) указано только три
обязательных аргумента. Однако при создании экземпляра можно задавать
любое число аргументов в соответствии с инструкцией по использованию
объекта. Доступ к аргументам осуществляется при помощи массива
arguments. При этом
В строках 12-14 первые три аргумента конструктора заносятся в свойства name, width и height.
В строке 08 определяется метод Show. Ниже в программе будет описана функция _Show, которая этот метод реализует (генерация HTML-кода построения меню).
В строке 17 описан метод Make. Метод реализуется при помощи
функции _Make. Видим, что метод объекта задается при помощи
команды присваивания:
В левой части размещается имя метода (с приставкой this.), а в правой имя функции, которая этот метод реализует. Чтобы было легко отличить функцию, реализующую метод объекта от обычной функции, рекомендуется вносить в имена функций-методов какую-то регулярную особенность. Например, начинать их со знака подчеркивания, за которым следует имя метода.
В строке 19 локальной переменной len присваивается число аргументов, переданное конструктору при создании экземпляра. Это число должно быть, нечетным. Если это не так, строка 20 отсекает последний аргумент.
Пусть, например, экземпляр задан кодом:
var menu1 = new Menu("menu1", 90, 70, "Волк", "./pic/wolf.gif", "Коза", "./pic/goat.gif", "Капуста", "./pic/cabbage.gif", "Лодка"); |
Меню будет построено только на три позиции (Лодка будет проигнорирована). Таким образом, строка 20 это попытка Ивана поставить в свой объект защиту от неверных данных. Отметим, что этот полезный фрагмент конструктора можно было бы и усилить.
В строках 23-25 происходит заполнение массива items данными, извлеченными из аргументов конструктора.
Этот метод генерирует HTML-код, который отображает меню на экране. Метод реализует функция _Show:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
function _Show() { var str="<FORM name="+this.name+"Form>"+ "<TABLE border=1 bgcolor="+this.bgcolor+ " cellspacing=0 cellpadding=10>"+ "<TR><TD>"+ "Ваш выбор:<BR>"+ "<SELECT name="+this.name+"Select"+ " onchange='"+this.name+".Make()'"+ " size="+this.items.length+">"; for(var ind=0; ind<this.items.length; ind++) str += "<OPTION"+(ind==this.mark ? " selected":"")+ ">"+ this.items[ind].name+"</OPTION>"; str += "</SELECT>"; str +="</TD><TD>"; str +="<IMG name="+this.name+"Img"+ " src="+this.items[this.mark].file+ " width="+this.width+" height="+this.height+ " border="+this.border+" alt=''>"; str +="</TD></TR></TABLE></FORM>"; document.write(str); this.Make(); } |
Например, для экземпляра:
var menu1 = new Menu("menu1", 90, 70, "Волк", "./pic/wolf.gif", "Коза", "./pic/goat.gif", "Капуста", "./pic/cabbage.gif", "Лодка", "./pic/ship.gif"); |
метод Show строит код, равнозначный следующему:
Посмотрите, как метод Show использует имя экземпляра, переданное через первый аргумент конструктора:
Этот метод является обработчиком события change в теге SELECT:
01 02 03 04 05 06 07 08 09 10 11 12 |
function _Make() { // Вычислить индекс отмеченной строки. var ind = document[this.name+"Form"][this.name+"Select"].selectedIndex; // Если отмечена новая строка, показать новую картинку. if(this.mark != ind) { document.images[this.name+"Img"].src=this.items[ind].file; this.mark = ind; } } |
Для экземпляра объекта с именем menu1 этот код будет эквивалентен такому:
function _Make() { // Вычислить индекс отмеченной строки. var ind = document.menu1Form.menu1Select.selectedIndex; // Если отмечена новая строка, показать новую картинку. if(this.mark != ind) { document.images.menu1Img.src=this.items[ind].file; this.mark = ind; } } |
Переменная mark хранит номер отмеченной строки, поэтому перерисовка картинки происходит лишь тогда, когда это действительно необходимо.
В строках 04-05 определяется индекс отмеченной строки. В строке 09 задается вывод новой картинки на экран. Для доступа к свойству src объекта соответствующему тегу IMG используется коллекция images.
![]() |