Кис Геннадий.
(с) АО Банкомсвязь. Программные Системы. 2000
г.
Дата последней модификации: Апрель 27, 2001
Рекомендации по использованию JavaScript (JScript 2)
на серверной части в технологии Active Server Page
предназначены главным образом в помощь
начинающим пользователям ASP, которым
приходится тратить многие часы за чтением MSDN.
Решения, изложенные в данном тексте, основаны на
опыте программирования автора, поэтому
рассматривайте их только как советы. Огромная
просьба, замечания направляйте по адресу gena@ps.bkc.com.ua
Для более детальной информации можно
использовать http://www.webreference.com/js/
Оглавление
Общие замечания
по JavaScript
1.
Использование переменных
2. Массивы
3. Функции
4. Классы
4.1. Наследование
4.2.
Переопределение функций
Замечания по ASP
1. Вызов
JavaScript скриптов в IIS 4.0 и ниже
2. Вызов
JavaScript скриптов в IIS 5.0 и выше
3.
Использование Session и Application
4. Оптимизация
5. Переносимость
Технология построения
пользовательского интерфейса с использованием
CGI протокола (редактируется)
- Определяйте переменные в функциях явным
образом с помощью оператора var, иначе возможно
использование переменной с таким же именем из
внешнего контекста и другие побочные явления.
Пример:
Неправильно:
function reverse(x) {
a = "";
i = x.length;
while (i > 0)
a += x.charAt(--i);
return a;
}
var a = “birth”;
var b = reverse (a);
теперь значение переменной a равно "htrib" |
Правильно: function reverse(x) {
var a = "";
var i = x.length;
while (i > 0)
a += x.charAt(--i);
return a;
}
var a = "birth";
var b = reverse (a); |
- Избегайте сложных имен переменных. Ошибку
неправильного имени переменной в некоторых
случаях очень тяжело заметить. При присвоении
переменной в результате ошибочного имени просто
создастся новая переменная, вместо изменения
имеющейся переменной. Также нужно избегать имен
с заглавными буквами, если это не несет
дополнительной семантики.
- Объявляйте переменную только когда готово
значение для нее. Объявление помещайте
максимально близко к моменту использования
переменной. Это правило позволит уменьшить
вероятность использования неинициализированных
переменных, либо случайного использования
переменных из внешнего контекста.
- Используйте локальные переменные, избегайте
глобальных. Локальные переменные
обрабатываются быстрее интерпретатором,
поскольку нет необходимости просматривать все
пространство имен.
- Помните, что переменные типа object хранят только ссылки на
объекты. Новый объект можно создать только
оператором new. При
присвоении объектной переменной другой
переменной копируются только ссылки на этот
объект, но не сами объекты. (Это касается только
пользовательских и COM-объектов. Встроенные
простые объекты типа String
или Number всегда
присваиваются по значению). Пример использования
объектов, заполнение массива комплексных чисел:
Неправильно: function Complex(re,im) {
this.re = 0;
this.im = 0;
}
var c = new Complex;
var dim = [];
for(var i=0;i<10;i++)
dim[i] = c;
c.re = 1;
c.im = -1;
В результате весь массив dim будет ссылаться
на один и тот же объект, содержащий
последнее установленное значение (1,-1). |
Правильно:
var dim = [];
for ( var i=0;i<10;i++)
dim[i] = new Complex; |
- В логических
операторах значения переменных null, undefined, "", 0 будут
рассматриваться как false.
Остальные значения вне зависимости от типа
рассматриваются как true.
Возвращаемым значением в операторе || будет
первое истинный аргумент, если такового нет, то
последний аргумент. Для оператора &&
наоборот, первый неистинный, иначе последний
аргумент. Пример:
- Избегайте изменения размера массивов. Это
приводит к медленным операциям с памятью.
Делайте более эффективную схему вставки
элементов в массив, желательно выделяйте сразу
необходимое число элементов.
- Использование аргументов по умолчанию.
JavaScript позволяет передавать только часть
параметров, опуская последние в списке. При этом
непереданные аргументы инициализируются
значением "undefined", что может быть
использовано для инициализации нужным значением
по умолчанию *.
Пример:
function Complex(re,im) {
this.re = re||0;
this.im = im||0;
}
- Возврат значений из функции. Здесь
необходимо отметить о свойстве авторазделения
команд в JavaScript по переводу строки. В связи с
этим, код
return
a + b;
будет проинтерпретирован как
return;
a + b;
Для программирования в объектном стиле можно
предложить следующие рекомендации.
- Определяйте функцию-конструктор класса
следующим образом:
function Class () {
// Attribute definitions
this.attribute1 = value1;
this.attribute2 = value2;
...
// Private attribute definition
var private1 = private_value1
...
// Method declaration
this.method1 = _method1;
this.method2 = _method2;
...
// Implementation
function _method1() {…}
function _method2() {…}
...
// Initialization
...
}
Тело функции-конструктора условно разбивается
на следующие секции:
- атрибуты общего доступа;
- закрытые атрибуты;
- декларация методов (функций-членов класса);
- секция реализации методов;
- секция инициализации – здесь помещайте код
логики по конструированию объекта
Эти требования только семантические и не
диктуются синтаксисом JavaScript, поэтому нужно
помнить, что следование этим простым правилам
упростит читабельность и сопровождение Ваших
программ.
- Для удобства используйте следующую нотацию. Функции-конструкторы
определяющие классы, именуйте с прописной буквы,
а обычные функции именуйте со строчной буквы.
- Функции, реализующие методы классов
(функции-члены), определяйте в теле
функции-конструктора класса для сокрытия
имен. Это позволит избежать конфликта одинаковых
имен функций при реализации методов разных
классов. Именуйте их так же, как имя
соответствующего метода с префиксом
подчеркиванием “_”. Возможно Вы предпочтете
вариант одинакового именования.
- Для коротких методов используйте
неименованные функции. Пример:
function Person() {
var name = "";
this.setName = function(a) { name = a;}
this.getName = function() {return name;}
}
- Закрытые атрибуты объявляются с помощью
обычного оператора var.
Доступ к закрытым атрибутам возможен только
через функции, определенные в секции реализации.
Для доступа к ним не надо (и не допустимо)
писать префикс this.
Это же касается и закрытых функций.
Наследование в JavaScript можно осуществить с
помощью прототипирования объекта.
Для этого объявите родительский класс,
объявите дочерний класс, создайте экземпляр
родительского класса и укажите его в качестве
прототипа для дочернего класса. Теперь при
создании объекта дочернего класса он будет
приобретать все свойства, включая фактические
значения атрибутов как у прототипа.
Пример:
function Person() {
this.name = “default”;
}
function Worker() {
this.speciality = “carpenter”;
}
Worker.prototype = new Person;
var w = new Worker;
Обработка доступа к функциям-членам и
атрибутам на JavaScript осуществляется следующим
образом: сначала идет поиск в самом объекте, если
нет, то поиск в прототипе объекта, если прототип
определен.
- Нельзя задавать прототип внутри самого
функции-конструктора. Самый первый объект при
создании не будет прототипирован. Кроме того, при
вызове каждого оператора new, будет
происходить прототипизация, что противоречит
самой концепции.
- Нужно иметь в виду, что при создании объекта,
имеющего прототип, конструктор прототипа не
вызывается, происходит простое копирование
значений атрибутов из прототипа.
- Приватные атрибуты базового класса ведут
себя как статические, т.е. их один экземпляр на
все объекты дочернего класса
Переопределить метод объекта можно в любой
момент выполнения. Если вы хотите переопределить
метод прототипа, то лучше всего сделать это в
конструкторе.
Пример:
function Parent() {
this.getInfo = function () { return " parent "; }
}
function Child1() {
}
function Child2() {
this.getInfo = function () { return " child "; }
}
var p = new Parent;
Child1.prototype = p;
Child2.prototype = p;
var list = [ new Child1, new Child2 ];
for (i=0; i < list.length; i++)
Response.Write ( list[i].getInfo() ) ;
Результат работы:
parent child
Чтобы вызвать перекрытый метод прототипа
используйте вызов через прототип. Пример:
function Child3() {
this.getInfo = function () { return Child3.prototype.getInfo() + " child
"; }
}
Если функция прототипа должна иметь доступ к
свойствам объекта (т.е. имеет обращения к this),
нужно поступить как в нижепреведенном примере:
function Child3() {
this.parent_getInfo = Child3.prototype.getInfo;
this.getInfo = function () { return this.parent_getInfo() + " child
"; }
}
Иначе, по this функция прототипа будет обращаться
к самому прототипу, а не к объекту.
ASP позволяет выделять общие данные для многих
гиперстраниц в отдельные файлы и
непостедственно включать их при обращении к
такой странице с помощью специальной директивы.
Можно использовать это свойство для реализации
модульного программирования на ASP.
- Чтобы включить скрипт на страницу,
используйте тег:
<!--#include
file=”script.inc”-->
при этом помните, что включаемый файл должен
быть полным html-тегом (т.е. все открытые теги должы
здесь и закрываться), поскольку он разбирается
точно так же как и простая html-страница, а
генерируемый вывод встраивается в текущий поток
вывода. Таким образом, тело скрипта должно быть
помещено в теги скрипта (<script></script> либо <%
%>)
- Директива включения (как и любая другая ) не
будет обрабатываться, если вы его включите ее в
тег скрипта (<script></script> либо <% %>). Будет
выдаваться ошибка синтаксиса при обработке
сервером.
- Не используйте рекурсивных, перекрестных либо
повторных включений файлов. Это может привести к
существенным проблемам. Например, нельзя
использовать конструкции вида:
script1.inc:
<!--#include file=”script2.inc”-->
script2.inc:
<!--#include file=”script1.inc”-->
- Руководствуйтесь следующим правилом: cкрипты
нижних уровней не должны включать скрипты.
Если необходимый код для работоспособности
скрипта находится в другом скрипте, включайте
оба этих скрипта в нужном порядке в вызывающем
модуле.
- Код во включаемом скрипте должен быть помещен в
тег <% %>.
Тег <script> нежелателен.
Используйте его для процедур клиента. Если вы его
используете, нужно указать явно язык скрипта и
место выполнения:
<script language=javascript
runat=server> иначе скрипт будет послан вместе со
страницей. Конечно, если ваши страницы
используют язык по умолчанию отличный от JavaScript,
то пригоден только этот тег.
В серверные теги <script>
можно включать только определения функций. Код,
не являющийся частью функций, может вызывать
непредсказуемые результаты, поскольку порядок
выполнения операторов в таком случае
непредсказуем (согласно MSDN).
Учитывая вышесказанное, еще раз отмечу, что если
в скрипте объявили класс с прототипом внутри
тега <script>, то
это приведет к тому, что прототип не будет
присвоен, а его методы будут недоступны. Это в
итоге вызовет труднообъяснимую ошибку времени
выполнения:
Неправильно: <script runat=server>
function ClassA() {…}
function ClassB() {…}
ClassB.prototype=new ClassA;
</script> |
Правильно: <%
function ClassA() {…}
function ClassB() {…}
ClassB.prototype=new ClassA;
%>
Примечание: Язык по
умолчанию
должен быть JavaScript |
Эта версии интернет-сервера позволяет
непосредственно подключать JavaScript файл с
помощью тега
<script src="script.js"
language=javascript runat=server>
при этом отпадают вышеперечисленные проблемы и
необходимость использования ASP-тегов в самих
включаемых файлах
Для хранения состояния приложения между
обработками запросов клиентов можно
пользоваться специальными коллекциями Session и
Application.
Ни один пользовательский объект, определенный
в скрипте не может быть полнофункционально
сохранен и использован в другом сеансе,
поскольку сохраняется только данные, но не
предкомпилированый код. Сам же функции объекта
будут разрушены после завершения скрипта, в
котором он был инициирован. При обращении к
методам этого объекта через переменную сессии
или приложения в другом сеансе будет возбуждена
исключительная ситуация:
JScript Run-time Errors 5011 "Can't execute code from a freed script"
Если все-таки нужно создавать свои объекты со
временем жизни больше скрипта, сохраняйте только
его состояние (в сессии, на странице, в куки или
базе данных), а потом конструируйте объект с
нужным состоянием.
Второй подход, это использование технологии MSC
(microsoft scripting components), которая позволяет легко
создавать COM-объекты из обычных ASP-скриптов.
Нельзя использовать в качестве прототипа
объект, сохраненный в коллекции Session или Application,
поскольку методы будут утеряны.
Оптимизация должна быть тщательно продумана. В
ASP-скриптах, как и в других языках, важно
определить какие части приложения потребляют
больше времени и ресурсов.
Несколько советов:
- Кеширование объектов и данных с областью
видимости приложения
. Создайте объект в Global.asa
или используйте создание по требованию и
сохранение в Application.
- Укрупнение вызовов Response.Write и буферизация
вывода. Буферизация включена по умолчанию в IIS
5.0. Для увеличения презентабельности приложений
с буферизованным выводом при выполнении
времениемких операций используйте периодически Response.Flush
для поддержания контакта с пользователем. Если
буферизацию не используете, уменьшите насколько
возможно число отдельных вызовов Response.Write.
Однако, если это сопряжено со сложными
строковыми преобразованиями, это может привести
к потере производительности.
- Используйте <object> тег вместо Server.CreateObject,
когда создаете объект времени жизни сессии или
приложения. Причина в том, что IIS реально создает
объект специфицированный тегом <object> по
его первому использованию. Если обращения к
объекту не было, то он вообще не
инициализируется.Server.CreateObject дает прямое
указание по созданию объекта.
- Проверки ввода осуществляйте на клиентской
части
, если это возможно, чтобы минимизировать
количество HTTP запросов. Все современные браузеры
позволяют это.
- Копируйте индивидуальные значения из коллекций
в локальные переменные
, если необходим доступ
к переменной более одного раза. Это избавляет ASP
от необходимости избыточного ассоциативного
поиска в коллекции.
- Отключайте поддержку сессий
, если приложение
не требует сессий. Сессии в IIS остаются в памяти, и
выделенная под нее память не будет освобождена
до явного ее закрытия или таймаута. Если
приложение многопользовательское, ресурсы могут
быть быстро исчерпаны и производительность
резко упадет. Если какие либо части приложения не
нуждаются в отдельной сессии, стоит отключить
ведение сессии для таких страниц с помощью
директивы @ENABLESESSIONSTATE.
Отключение сессии особенно полезно, если
страница содержит элемент <frameset> элемент. Некоторые
браузеры, включая Internet Explorer, будут
использовать отдельную нить для обработки
каждого фрейма набора. Если сессия будет
включена, то выигрыш от параллельной обработки
фреймов на клиенте будет потерян, поскольку IIS
будет вынужден обрабатывать запросы
последовательно.
- При использовании сессий минимизируйте данные,
хранящиеся на уровне сессии
. Чтобы уменьшить
расход памяти.
- Не объявляйте пустые Session_OnStart или Session_OnEnd.
- Используйте режим внешнего процесса (out-of-process)
только в целях отладки.
Если приложение
достаточно отлажено и стабильно, более
производительно не создавать отдельных
процессов для выполнения скриптов, а выполнять
их прямо в процессе IIS.
JavaScript может испольоваться не только в ASP.
Часто хотелось бы достичь переносимости кода на
другие интерпретаторы, например, WSH (microsoft windows
scripting host, позволяющий писать достаточно мощные
консольные и даже оконные приложения) с
минимальными затратами на переделку кода, а
еще лучше раздлять использование общего кода.
Несколько практических рекомендаций по этому
вопросу.
- Стройте приложение на идее разделяемых
объектов или функций. На основе таких базовых
объектов Вы сможете легко модифицировать и
расширять свое приложение. Разделите код
пользовательского интерфейса и логики
приложения в разные скрипты . Для удобства
помещайте ASP в файлы с расширением asp,
а объекты логики в файлы с расширением js. Не
используйте ASP и HTML-тегов в
скриптах логики, а также встроенных в ASP
объектов и функций, иначе они не смогут корректно
обрабатываться другими интерпретаторами. То
есть, это должен быть чистый JavaScript.
- Для включения js-скриптов в asp-скрипты
используйте тег <script
src=script.js language="javascript"> </script>. Если у
вас IIS 4.0 или ниже, к сожалению параметр src
не поддерживается. В этом случае скрипт можно
подключить только директивой <!--#include file=script.js-->, а в него
необходимо обязательно поместить ASP
теги <script> либо <% %>. Возникает противоречие,
с одной стороны, в скрипт нельзя включать ASP
теги из-за требования переносимости, с другой
стороны, это обязательное требование ASP
интерпретатора. Для решения этой проблемы код
включаемого скрипта оформите следующим образом:
//<%
Ваш JavaScript код
//%>
При этом возникает побочный эффект - появление //
в выходном потоке для каждого включаемого
скрипта. Очевидное решение -поместить директивы
включения файлов в комментарий:
<%="<!--"%>
<!--#include file=script1.js -->
<!--#include file=script2.js -->
...
<%="-->"%>
В основу положен событийно-ориентированный
подход, где события инициируются пользователем
через интерфейс гипертекстовых форм.
Главное отличие от других типов построения
клиента – это асинхронность. Сервер не может
предполагать, что пользовательский интерфейс
находится в определенном состоянии. Из-за
гипертекстности и использования кеша страниц
состояние приложения на клиентской части может
быть легко рассинхронизировано с состоянием на
сервере.
Таким образом, запрос от клиента должен
содержать не только имя объекта, к которому
производится доступ, и требуемое от него
действие (если объект позволяет несколько
операций), но и текущее состояние этого объекта.
Для эффективной работы все же рекомендуется
хранить часть объектов на сервере для
эффективного доступа к ним, к таким объектам
можно отнести выборки из баз данных, редко
меняющиеся списки. Кроме того, их нужно разделять
для доступа между несколькими пользователями.