Клиентский JavaScript 1.3 Руководство

         

Более гибкие конструкторы


Показанная функция-конструктор не даёт возможности создавать экземпляры. Как и в Java, Вы можете предоставлять аргументы конструктору для инициализации значений свойств экземпляров. На рисунке показан один из способов сделать это.



Добавление и удаление свойств



Добавление и удаление свойств

В языках на основе классов Вы обычно создаёте класс на этапе компиляции, а затем инстанциируете экземпляры на этапе компиляции или на этапе прогона. Вы не можете изменить количество или тип свойств класса, после того как Вы его определили. В JavaScript, однако, Вы можете на этапе прогона добавлять или удалять свойства любого объекта. Если Вы добавляете свойство объекту, который используется как прототип для набора объектов, то объекты, для которых данный объект является прототипом, также получают новое свойство.



Добавление свойств



Добавление свойств

В JavaScript Вы можете добавить свойства любому объекту на этапе прогона. Вы не ограничены использованием только свойств, предоставленных функцией-конструктором. Для добавления свойств, специфичных для отдельного объекта, Вы присваиваете значение объекту:

mark.bonus = 3000;

Теперь объект mark имеет свойство bonus, но другие WorkerBee этого свойства не имеют.

Если Вы добавляете новое свойство объекту, который используется как функция-конструктор, Вы добавляете это свойство всем объекта, наследующим свойства от данного прототипа. Например, Вы можете добавить свойство specialty ко всем employees следующим оператором:

Employee.prototype.specialty = "none";

Как только JavaScript выполнит этот оператор, объект mark также получит свойство specialty со значением "none". На рисунке показан эффект от добавления этого свойства прототипу Employee и последующее переопределение его для прототипа Engineer.



Объектная Модель. Детали.



Глава 8

Объектная Модель. Детали.

JavaScript это язык на базе прототипов, а не на базе классов. Из-за этого в JavaScript менее очевидно, как создаётся иерархия объектов и происходит наследование свойств и их значений. В данной главе делается попытка прояснит этот вопрос.

Предполагается, что Вы уже немного знакомы с JavaScript и что Вы использовали функции JavaScript для создания простых объектов.

В главе имеются следующие разделы:

Языки на базе классов и языки на базе прототипов

Пример EmployeeСоздание иерархии

Свойства объектаБолее гибкие конструкторы

И снова о наследовании свойств



Глобальная информация в конструкторах



Глобальная информация в конструкторах

При создании конструкторов Вы должны быть осторожны, если устанавливаете глобальную информацию в конструкторе. Например, у Вас имеется уникальный идентификатор ID, присваиваемый автоматически каждому новому employee. Вы может использовать такое определение для Employee:

var idCounter = 1;function Employee (name, dept) {
this.name = name || "";
   this.dept = dept || "general";
   this.id = idCounter++;
}

Теперь, ели Вы создаёте новый Employee, конструктор присваивает ему следующий ID и выполняет инкремент глобального счётчика ID. Поэтому, если Ваш следующий оператор будет таким, как ниже приведённый, victoria.id будет 1, а harry.id будет 2:

victoria = new Employee("Pigbert, Victoria", "pubs")
harry = new Employee("Tschopik, Harry", "sales")

На первый взгляд всё отлично. Однако idCounter увеличивается всякий раз при создании Employee-объекта. Если Вы создаёте всю иерархию Employee, показанную в данной главе, Employee-конструктор вызывается при каждой установке прототипа. Предположим, у Вас имеется такой код:

var idCounter = 1;function Employee (name, dept) {
   this.name = name || "";
   this.dept = dept || "general";
   this.id = idCounter++;
}function Manager (name, dept, reports) {...}
Manager.prototype = new Employee;function WorkerBee (name, dept, projs) {...}
WorkerBee.prototype = new Employee;function Engineer (name, projs, mach) {...}
Engineer.prototype = new WorkerBee;function SalesPerson (name, projs, quota) {...}
SalesPerson.prototype = new WorkerBee;mac = new Engineer("Wood, Mac");

Предположим далее, что отсутствующие здесь определения имеют свойство base и вызывают конструктор выше себя в цепочке конструкторов. Тогда к моменту создания объекта mac  mac.id будет  5.

В зависимости от приложения может или может не быть важно, увеличивается ли счётчик на это дополнительное количество раз. Если Вам необходимо точное значение счётчика, одним из возможных решений может быть использование следующего конструктора:

function Employee (name, dept) {
   this.name = name || "";
   this.dept = dept || "general";
   if (name)
      this.id = idCounter++;
}

Если Вы создаёте экземпляр Employee для использования его в качестве прототипа, Вы не предоставляете аргументы конструктору. Используя данное определение конструктора и не предоставляя аргументов, конструктор не присваивает значение id и не обновляет счётчик. Следовательно, чтобы Employee получил присвоенный id, Вы обязаны специфицировать имя employee. В данном примере, mac.id может быть 1.



И снова о наследовании свойств


В предыдущем разделе показано, как реализуются иерархия и наследование в конструкторах и прототипах JavaScript.
В данном разделе обсуждаются некоторые тонкости, не очевидные при предыдущем рассмотрении.



Языки на базе классов и языки на базе прототипов


Объектно-ориентированные языки на базе классов, такие как Java и C++, помогут понять две разных сущности: класс и экземпляр.

Класс определяет все свойства (методы и поля - в Java, члены, то есть свойства, в C++), характеризующие определённый набор объектов. Класс это абстракция, в отличие от конкретного члена набора объектов, который он описывает. Например, класс Employee может представлять набор всех employees/служащих.

Экземпляр, с другой стороны, это инстанциация класса; то есть это один из членов класса. Например, Victoria может быть экземпляром класса Employee, представляя конкретную персону как employee. Экземпляр имеет в точности все свойства своего класса-родителя (ни более, ни менее).

В языке на базе прототипов, таком как JavaScript, нет такого отличия: здесь просто имеются объекты. В языке на базе прототипов имеется понятие

объект-прототип\prototypical object, это объект, используемый как шаблон, по которому получаются начальные свойства объекта. Любой объект может специфицировать свои собственные свойства либо при создании, либо на этапе прогона. Кроме того, любой объект может быть ассоциирован как прототип другого объекта для совместного использования свойств первого объекта.



Локальные и наследуемые значения



Локальные и наследуемые значения

Когда Вы выполняете доступ к свойству объекта, JavaScript выполняет следующие шаги, как уже было показано в этой главе:

Проверяется, существует ли значение локально. Если да, это значение возвращается.

Если локального значения нет, проверяется цепочка прототипов (через использование свойства __proto__).

Если объект в цепочке прототипов имеет значение для специфицированного свойства, это значение возвращается.Если такое свойство не найдено, объект не имеет данного свойства.

Результат выполнения этих шагов зависит от того, каковы были определения.

Оригинальный пример имел такие определения:

function Employee () {
this.name = "";
this.dept = "general";
}function WorkerBee () {
this.projects = [];
}
WorkerBee.prototype = new Employee;

С помощью этих определений Вы, предположим, создаёте amy как экземпляр WorkerBee таким оператором:

amy = new WorkerBee;

Объект amy имеет одно локальное свойство, projects. Значения свойств name и dept не являются локальными по отношению к amy и поэтому получены из свойства __proto__ объекта amy.
Таким образом, amy имеет следующие значения свойств:

amy.name == "";
amy.dept = "general";
amy.projects == [];

Теперь предположим, что Вы изменяете значение свойства name в прототипе, ассоциированном с Employee:

Employee.prototype.name = "Unknown"

На первый взгляд можно ожидать, что новое значение передаётся всем экземплярам Employee. Однако это не так.

Когда Вы создаёте любой экземпляр объекта Employee, этот экземпляр получает локальное значение свойства name (пустую строку). Это означает, что, если Вы устанавливаете прототип WorkerBee через создание нового объекта Employee, WorkerBee.prototype имеет локальное значение для свойства name. Следовательно, когда JavaScript ищет свойство name объекта amy (экземпляра WorkerBee), JavaScript находит значение для этого свойства в WorkerBee.prototype. Он, следовательно, не просматривает далее цепочку до Employee.prototype.

Если Вы хотите изменить значение свойства объекта на этапе прогона и иметь новое значение наследуемым всеми потомками этого объекта, Вы не определяете это свойство в функции-конструкторе объекта. Вместо этого Вы добавляете это свойство в ассоциированный прототип конструктора. Например, Вы изменяете предыдущий код на такой:

function Employee () {
this.dept = "general";
}
Employee.prototype.name = "";function WorkerBee () {
this.projects = [];
}
WorkerBee.prototype = new Employee;amy = new WorkerBee;Employee.prototype.name = "Unknown";

Теперь свойство name объекта amy становится "Unknown".

Как видно из этих примеров, если Вы хотите иметь значения по умолчанию для свойств объекта и хотите иметь возможность изменять значения по умолчанию на этапе прогона программы, Вы должны устанавливать свойства в прототипе конструктора, а не в самой функции-конструкторе.



Наследование свойств


Наследование свойств

Предположим, Вы создаёте объект mark как WorkerBee, как показано на Рисунке 8.3, следующим оператором:

mark = new WorkerBee;

Когда JavaScript видит операцию new, он создаёт новый родовой/generic объект и передаёт этот новый объект как значение ключевого слова this функции-конструктору WorkerBee. Функция-конструктор явным образом устанавливает свойства projects. Она также устанавливает в значение внутреннего свойства __proto__ значение WorkerBee.prototype. (Имя этого свойства имеет по два символа подчёркивания в начале и в конце.) Свойство __proto__ определяет цепочку прототипов, используемую для возвращения значения свойства. После установки этих свойств JavaScript возвращает новый объект, и операция присвоения устанавливает переменную mark в этот объект.

Этот процесс не помещает явным образом значения в объект mark (локальные значения) для свойств, которые mark наследует из цепочки прототипов. Когда Вы запрашиваете значение свойства, JavaScript сначала проверяет, существует ли значение в данном объекте. Если значение существует, оно возвращается. Если значение локально отсутствует, JavaScript проверяет цепочку прототипов (используя свойство __proto__). Если объект в цепочке прототипов имеет значение для этого свойства, возвращается это значение. Если такое свойство не найдено, JavaScript сообщает, что такого свойства у объекта нет. Отсюда, объект mark имеет следующие свойства и значения:

mark.name = "";
mark.dept = "general";
mark.projects = [];

Объект mark наследует значения свойств name и dept из объекта-прототипа в mark.__proto__. Локальное значение свойства projects присваивается конструктором WorkerBee. Это даёт Вам наследование свойств и их значений в JavaScript. Некоторые тонкости этого процесса обсуждаются в разделе "И Снова о Наследовании Свойств".

Поскольку эти конструкторы не предоставляют поддержки значений, специфичных для экземпляра, эта информация является общей/generic. Значения свойств являются значениями по умолчаниями, используемыми всеми новыми объектами, создаваемыми из WorkerBee. Вы можете, конечно, изменить значение любого из этих свойств. Так, Вы можете задать специфическую информацию для mark таким образом:

mark.name = "Doe, Mark";
mark.dept = "admin";
mark.projects = ["navigator"];



Определение класса


Определение класса

В языках на базе классов Вы определяете класс в отдельном определении класса\class definition. Здесь Вы можете специфицировать специальные методы, называемые конструкторами, которые создают экземпляры данного класса. Метод-конструктор может специфицировать начальные значения свойств экземпляров и выполнять иную работу, необходимую на этапе создания экземпляров. Вы используете операцию new вместе с методом-конструктором для создания экземпляров класса.

В JavaScript используется похожая модель, но отсутствует определение класса, отдельное от конструктора. Вместо этого Вы определяете функцию-конструктор для создания объектов с определённым набором начальных значений и свойств. Любая функция JavaScript может использоваться как конструктор. Вы используете операцию new вместе с функцией-конструктором для создания нового объекта.



Определение взаимоотношений экземпляров



Определение взаимоотношений экземпляров

Возможно, Вы захотите узнать, какие объекты находятся в цепочке прототипов данного объекта, чтобы определить, из какого объекта данный объект наследует свойства. В языках на базе классов Вы можете использовать для этого операцию instanceof. В JavaScript нет instanceof, но Вы сами можете написать такую функцию.

Как сказано в "Наследовании Свойств", если Вы используете операцию new с функцией-конструктором для создания новых объектов, JavaScript устанавливает в свойство __proto__ нового объекта значение свойства prototype функции-конструктора. Вы можете использовать это для проверки цепочки прототипов.

Например, у Вас есть уже показанный ранее набор определений с соответственно установленными прототипами. Создайте объект __proto__ так:

chris = new Engineer("Pigman, Chris", ["jsd"], "fiji");

С эти объектом все следующие операторы true:

chris.__proto__ == Engineer.prototype;
chris.__proto__.__proto__ == WorkerBee.prototype;
chris.__proto__.__proto__.__proto__ == Employee.prototype;
chris.__proto__.__proto__.__proto__.__proto__ == Object.prototype;
chris.__proto__.__proto__.__proto__.__proto__.__proto__ == null;

Имея это, Вы можете написать функцию instanceOf:

function instanceOf(object, constructor) {
   while (object != null) {
      if (object == constructor.prototype)
         return true;
      object = object.__proto__;
   }
   return false;
}

При таком определении все следующие выражения true:

instanceOf (chris, Engineer)
instanceOf (chris, WorkerBee)
instanceOf (chris, Employee)
instanceOf (chris, Object)

Но следующее выражение будет false:

instanceOf (chris, SalesPerson)



Отличия. Резюме.



Отличия. Резюме.

В таблице дано краткое резюме по некоторым отличительным особенностям языков. Остальная часть данной главы посвящена деталям использования конструкторов и прототипов языка JavaScript для создания иерархии и делаются сравнения с аналогичными действиями в Java.



Подклассы и наследование


Подклассы и наследование

В языках на основе классов Вы создаёте иерархию классов через определения классов. В определении класса Вы можете специфицировать, что новый класс является подклассом\subclass уже существующего класса. Подкласс наследует все свойства своего родителя (суперкласса) и может добавлять новые свойства или изменять наследуемые. Например, класс Employee имеет только свойства name и dept, а Manager является подклассом класса Employee и добавляет свойство reports. В этом случае экземпляры класса Manager будут иметь три свойства: name, dept и reports.

В JavaScript реализовано наследование, что даёт возможность ассоциировать объект-прототип с любой функцией-конструктором. Так, Вы можете воспроизвести тот же пример Employee-Manager, но используя несколько иную терминологию. Сначала Вы определяете функцию-конструктор Employee, специфицируя свойства name и dept. Затем Вы определяете функцию-конструктор Manager, специфицируя свойство reports. Наконец, Вы присваиваете новый объект Employee как prototype функции-конструктору Manager. Затем создаёте новый объект Manager, который наследует свойства name и dept из объекта Employee.



Пример Employee


В остальной части данной главы используется иерархия employee, показанная на рисунке.



Простая иерархия объектов



Рисунок 8.1   Простая иерархия объектов

В этом примере используются следующие объекты:

Employee имеет свойства name (значение по умолчанию - пустая строка) и dept (значение по умолчанию - "general").

Manager базируется на Employee. Он добавляет свойство reports (значение по умолчанию - пустой массив, предназначенный для хранения массива Employee-объектов как значений).

WorkerBee также основан на Employee. Он добавляет свойство projects (значение по умолчанию - пустой массив, предназначенный для хранения массива строк как значений).

SalesPerson базируется на WorkerBee. Он добавляет свойство quota (значение по умолчанию - 100). Также переопределяет свойство dept значением "sales", указывая, что все менеджеры по продажам/salespersons находятся в этом отделе.

Engineer базируется на WorkerBee. Он добавляет свойство machine (значение по умолчанию - пустая строка), а также переопределяет свойство dept property значением "engineering".



Определения объекта Employee



Рисунок 8.2   Определения объекта Employee

Следующие определения Employee в Java и в JavaScript похожи. Единственное отличие - Вы должны специфицировать тип каждого свойства в Java, но не в JavaScript, и Вы должны создать конкретный метод-конструктор для Java-класса.

JavaScript

Java

function Employee () {
this.name = "";
this.dept = "general";

}

public class Employee {
   public String name;

   public String dept;
   public Employee () {

      this.name = "";

      this.dept = "general";
   }
}

Определения для Manager и WorkerBee показывают отличия в специфицировании объекта, стоящего выше в цепочке иерархии. В JavaScript Вы добавляете экземпляр-прототип как значение свойства prototype функции-конструктора. Вы можете сделать это в любое время после определения конструктора. В Java Вы специфицируете суперкласс в определении класса. Вы не можете изменить суперкласс вне определения класса.

JavaScript

Java

function Manager () {
this.reports = [];
}
Manager.prototype = new Employee;function WorkerBee () {
this.projects = [];
}
WorkerBee.prototype = new Employee;
public class Manager extends Employee {
   public Employee[] reports;
   public Manager () {
      this.reports = new Employee[0];
   }
}public class WorkerBee extends Employee {
   public String[] projects;
   public WorkerBee () {
      this.projects = new String[0];
   }
}

Определения Engineer и SalesPerson создают объекты, которые являются потомками WorkerBee и, следовательно, потомками Employee. Объект этих типов имеет свойства всех объектов, стоящих выше него в цепочке иерархии. Кроме того, эти определения переопределяют наследуемое значение свойства dept новыми значениями, специфичными для этих объектов.


JavaScript

Java
function SalesPerson () {
   this.dept = "sales";
   this.quota = 100;
}
SalesPerson.prototype = new WorkerBee;function Engineer () {
   this.dept = "engineering";
   this.machine = "";
}
Engineer.prototype = new WorkerBee;
public class SalesPerson extends WorkerBee {
   public double quota;
   public SalesPerson () {
      this.dept = "sales";
      this.quota = 100.0;
   }
}public class Engineer extends WorkerBee {
   public String machine;
   public Engineer () {
      this.dept = "engineering";
      this.machine = "";
   }
}
Используя эти определения, Вы можете создавать экземпляры этих объектов, которые получают значения по умолчанию своих свойств. Рисунок 8.3 иллюстрирует использование этих определений JavaScript для создания новых объектов и показывает значения свойств новых объектов.

ПРИМЕЧАНИЕ:

Термин экземпляр\instance имеет специфическое техническое значение в языках программирования на базе классов. В них экземпляр это отдельный член класса, фундаментально отличающийся от класса. В JavaScript "экземпляр/instance" не имеет этого технического значения, поскольку JavaScript не различает классы и экземпляры. Однако, говоря о JavaScript, "экземпляр" может использоваться неформально для обозначения объекта, созданного с использованием конкретной функции-конструктора. Так, в данном примере, Вы можете неформально сказать, что jane это Engineer-экземпляр. Аналогично, хотя термины parent\родитель, child\дочерний, ancestor\предок и descendant\потомок не имеют формальных значений в JavaScript, Вы можете использовать их неформально для обращения к объектам выше или ниже по цепочке прототипов.


Создание объектов с помощью простых определений



Рисунок 8.3   Создание объектов с помощью простых определений



В таблице показаны определения Java


Рисунок 8.5   Специфицирование свойств в конструкторе, этап 1



В таблице показаны определения Java и JavaScript для этих объектов.

JavaScript

Java
function Employee (name, dept) {
this.name = name || "";
this.dept = dept || "general";
}
public class Employee {
   public String name;
   public String dept;
   public Employee () {
      this("", "general");
   }
   public Employee (name) {
      this(name, "general");
   }
   public Employee (name, dept) {
      this.name = name;
      this.dept = dept;
   }
}
function WorkerBee (projs) {
this.projects = projs || [];
}
WorkerBee.prototype = new Employee;
public class WorkerBee extends Employee {
   public String[] projects;
   public WorkerBee () {
      this(new String[0]);
   }
   public WorkerBee (String[] projs) {
      this.projects = projs;
   }
}
function Engineer (mach) {
   this.dept = "engineering";
   this.machine = mach || "";
}
Engineer.prototype = new WorkerBee;
public class Engineer extends WorkerBee {
   public String machine;
   public WorkerBee () {
      this.dept = "engineering";
      this.machine = "";
   }
   public WorkerBee (mach) {
      this.dept = "engineering";
      this.machine = mach;
   }
}
Эти определения JavaScript используют специальную идиому для установки значений по умолчанию:



this.name = name || "";

Логическая операция ИЛИ (||) в JavaScript вычисляет первый аргумент. Если он конвертируется в true, операция возвращает его. Иначе операция возвращает значение второго аргумента. Следовательно, эта строка кода проверяет, имеет ли name подходящее значение для свойства name. Если имеет, это значение устанавливается в this.name. Иначе - в this.name устанавливается пустая строка. Эта идиома используется в данной главе для краткости; однако она может, на первый взгляд, показаться непонятной.

При помощи этих определений Вы можете при создании объекта специфицировать значения локально определяемых свойств. Как видно на Рисунке 8.5, Вы можете использовать следующий оператор для создания нового Engineer:

jane = new Engineer("belau");

Jane-свойства будут теперь:

jane.name == "";
jane.dept == "general";
jane.projects == [];
jane.machine == "belau"

Заметьте, что с помощью этих определений Вы не можете специфицировать начальное значение наследуемого свойства, такого как name. Если Вы хотите специфицировать начальные значения наследуемых свойств в JavaScript, Вы должны добавить дополнительный код в функцию-конструктор.

Итак, функция-конструктор создала общий (родовой/generic) объект и затем специфицировала локальные свойства и значения для этого нового объекта. Вы можете заставить конструктор добавить дополнительные свойства, непосредственно вызвав функцию-конструктор для объекта, стоящего выше в цепочке прототипов. На рисунке показаны эти новые определения.


Рассмотрим одно из этих определений


Рисунок 8.6   Специфицирование свойств в конструкторе, этап 2



Рассмотрим одно из этих определений детально. Вот новое определение для конструктора Engineer:

function Engineer (name, projs, mach) {
this.base = WorkerBee;
this.base(name, "engineering", projs);
this.machine = mach || "";
}

Предположим, Вы создаёте новый Engineer-объект:

jane = new Engineer("Doe, Jane", ["navigator", "javascript"], "belau");

JavaScript выполняет следующее:



Операция new создаёт общий объект и присваивает его свойство __proto__ Engineer.prototype.

Операция new передаёт этот новый объект Engineer-конструктору как значение ключевого слова this.

Конструктор создаёт новое свойство base для этого объекта и присваивает значение конструктора WorkerBee свойству base. Это делает конструктор WorkerBee методом Engineer-объекта.

Имя свойства base не является специальным. Вы может использовать любое верное имя свойства; base просто подходит по смыслу.

Конструктор вызывает метод base, передавая ему в качестве аргументов два аргумента из переданных конструктору ("Doe, Jane" и ["navigator", "javascript"]), а также строку "engineering". Явное использование "engineering" в конструкторе указывает, что все Engineer-объекты имеют одно значение для наследуемого свойства dept, и что это значение переопределяет значение, наследуемое из Employee.

Поскольку base это метод из Engineer, внутри вызова base JavaScript связывает ключевое слово this с объектом, созданным на Этапе 1. Таким образом, функция WorkerBee в свою очередь передаёт аргументы "Doe, Jane" и ["navigator", "javascript"] в функцию-конструктор Employee. После возвращения из функции-конструктора Employee, функция WorkerBee использует оставшийся аргумент для установки свойства projects.

После возвращения из метода base, конструктор Engineer инициализирует свойство machine объекта значением "belau".



После возвращения из конструктора, JavaScript присваивает новый объект переменной jane.

Вы можете подумать, что, вызвав конструктор WorkerBee из конструктора Engineer, Вы установили соответствующее наследование для Engineer-объектов, но это не так. Вызов конструктора WorkerBee гарантирует, что объект Engineer стартует со свойствами, специфицированными во всех функциях-конструкторах, которые вызываются. Однако, если Вы позднее добавите свойства в прототипы Employee или WorkerBee, эти свойства не будут наследоваться объектом Engineer. Например, у Вас имеются операторы:

function Engineer (name, projs, mach) {
this.base = WorkerBee;
this.base(name, "engineering", projs);
this.machine = mach || "";
}
jane = new Engineer("Doe, Jane", ["navigator", "javascript"], "belau");
Employee.prototype.specialty = "none";

Объект jane не наследует свойство specialty. Вам всё ещё необходимо явным образом изменить прототип для обеспечения динамического наследования. предположим, что у Вас имеются другие операторы:

function Engineer (name, projs, mach) {
this.base = WorkerBee;
this.base(name, "engineering", projs);
this.machine = mach || "";
}
Engineer.prototype = new WorkerBee;
jane = new Engineer("Doe, Jane", ["navigator", "javascript"], "belau");
Employee.prototype.specialty = "none";

Теперь значение свойства specialty объекта jane равно "none".


Создание иерархии


Есть несколько способов создания функции-конструктора для реализации иерархии Employee. Выбор конкретного способа во многом зависит от того, что должно будет делать Ваше приложение.

В данном разделе показано, как использовать очень простое (и сравнительно негибкое) определение для демонстрации создания работающей иерархии. В этих определениях Вы не можете специфицировать значения свойств, когда создаёте объект. Вновь создаваемый объект просто получает все значения по умолчанию, которые Вы можете изменить позднее. Рисунок 8.2 показывает иерархию с несколькими простыми определениями.

В реальном приложении, Вы, вероятно, определите конструктор, который позволит задавать начальные значения свойств на этапе создания объекта (см. "Более Гибкие Конструкторы"). Итак, эти простые определения демонстрируют появление иерархии.



Свойства объекта


В этом разделе обсуждается, как объекты наследуют свойства других объектов в цепочке прототипов и что происходит при добавлении свойства на этапе прогона.



Сравнение объектных


Таблица 8.1 Сравнение объектных систем языков на базе классов (Java)
и языков на базе прототипов (JavaScript)

На базе классов (Java)

На базе прототипов (JavaScript)

Класс и экземпляр это разные сущности.Все объекты являются экземплярами.
Класс определяется в определении класса; инстанциация (создание экземпляров) производится методами-конструкторами.Набор объектов создаётся и определяется функциями-конструкторами.
Одиночный объект создаётся операцией new.То же самое.
Иерархия объектов создаётся через использование определения класса для определения подклассов существующих классов.Иерархия объектов создаётся путём присвоения объекта как прототипа, ассоциированного с функцией-конструктором.
Свойства наследуются по цепочке классов.Свойства наследуются по цепочке прототипов.
Определение класса специфицирует все свойства всех экземпляров данного класса. Свойства нельзя добавлять динамически на этапе прогона.Функция-конструктор или прототип специфицируют начальный набор свойств. Свойства могут добавляться динамически отдельному объекту или целому набору объектов.



Внедрение JavaScript в HTML



Глава 9
Внедрение JavaScript в HTML

Вы можете встроить JavaScript в HTML-документ: как операторы и функции в тэге <SCRIPT>, специфицировав файл в качестве источника кода JavaScript, специфицировав выражения JavaScript как значения HTML-атрибута или как обработчики событий в некоторых HTML-тэгах (во основном - в элементах формы).

В главе имеются следующие разделы:

Использование тэга SCRIPT

Специфицирование файла с кодом JavaScript

Использование выражений JavaScript в качестве значений атрибутов HTML

Использование кавычек

Специфицирование альтернативного содержимого тэгом NOSCRIPT

О скриптинге в обработчиках событий см. Главу 10, "Обработчики Событий".

ПРИМЕЧАНИЕ: В отличие от HTML, JavaScript чувствителен к регистру символов.



Использование кавычек


Везде, где Вы указываете строку в кавычках внутри строкового литерала, используйте одинарные кавычки (') для ограничения строкового литерала. Это даст скрипту возможность отличить литерал внутри строки. В следующем примере функция bar содержит литерал "left" в значении атрибута, заключённого в двойные кавычки:

function bar(widthPct) {
document.write("<HR ALIGN='left' WIDTH=" + widthPct + "%>")
}

Вот другой пример:

<INPUT TYPE="button" VALUE="Press Me" onClick="myfunc('astring')">



Использование тэга SCRIPT


Тэг <SCRIPT> является расширением HTML и может содержать любое количество операторов JavaScript, как показано здесь:

<SCRIPT>
операторы JavaScript ...
</SCRIPT>

В документе может быть несколько тэгов <SCRIPT>, и каждый тэг может содержать любое количество операторов JavaScript.



Использование выражений JavaScript в качестве значений атрибутов HTML


Используя мнемоники JavaScript, Вы можете специфицировать любое выражение JavaScript в качестве значения атрибута HTML. Значения мнемоник вычисляются динамически. Это позволяет создавать более гибкие HTML-конструкции, поскольку  атрибуты одного HTML-элемента могут зависеть от информации об элементах, размещённых ранее на странице.

Вы, возможно, уже знакомы с мнемониками HTML, с помощью которых можно определить символы по специальному числовому коду или имени, вставив впереди амперсанд (&) и в конце мнемоники - точку с запятой (;). Например, Вы можете включить символ "больше" (>) мнемоникой &gt;, а символ "меньше" (<) мнемоникой &lt;.

Мнемоники JavaScript также начинаются знаком амперсанда (&) и заканчиваются точкой с запятой (;). Вместо имени или числа Вы можете использовать выражение JavaScript, заключённое в фигурные скобки {}. Мнемоники JavaScript можно использовать только в тех местах, где может появиться обычное значение атрибута HTML. Например, предположим, Вы определяете переменную barWidth. Вы можете создать горизонтальную линию со специфицируемым процентным значением:

<HR WIDTH="&{barWidth};%" ALIGN="LEFT">

Так, например, если barWidth будет 50, этот оператор создаст такую линию.



Показано определение функций три раза,


Показано определение функций три раза, первый - для JavaScript 1.0, второй - использование возможностей JavaScript 1.1, третий - JavaScript 1.2.
<SCRIPT LANGUAGE="JavaScript">
// Определить функции, совместимые с версией 1.0, такие как doClick() в этом примере
</SCRIPT><SCRIPT LANGUAGE="JavaScript1.1">
// Переопределение этих функций с использованием возможностей 1.1
// Также определение функций только версии 1.1
</SCRIPT><SCRIPT LANGUAGE="JavaScript1.2">
// Переопределение этих функций с использованием возможностей 1.2
// Также определение функций только версии 1.2
</SCRIPT><FORM ...>
<INPUT TYPE="button" onClick="doClick(this)" ...>
...
</FORM>

Показано использование двух разных версий


Показано использование двух разных версий документа JavaScript, одна - для JavaScript 1.1, а вторая - для JavaScript 1.2. По умолчанию загружается документ для JavaScript 1.1. Если у пользователя запущен Navigator 4.0, метод replace заменяет страницу.
<SCRIPT LANGUAGE="JavaScript1.2">
// Заменить эту страницу в истории сессии версией для 1.2
location.replace("js1.2/mypage.html");
</SCRIPT>
[1.1-совместимая страница продолжается здесь...]

здесь нужно проверить версию jsVersion


Показано, как тестировать свойство navigator.userAgent для определения версии Navigator'а 4.0. Затем код выполняет версии 1.1 и 1.2.
<SCRIPT LANGUAGE="JavaScript">
if (navigator.userAgent.indexOf("4.0") != -1)
jsVersion = "1.2";
else if (navigator.userAgent.indexOf("3.0") != -1)
   jsVersion = "1.1";
else
   jsVersion = "1.0";
</SCRIPT>
[ здесь нужно проверить версию jsVersion перед использованием расширения 1.1 или 1.2]

первый скрипт



Пример: первый скрипт

На Рисунке 9.1 показан простой скрипт, выводящий в Navigator'е:

Hello, net!
That's all, folks.

Заметьте, что здесь нет отличий в отображении первой строки, генерируемой JavaScript, и второй строки, генерируемой обычным HTML.



Простой скрипт


Рисунок  9.1  Простой скрипт

Вы иногда видите точку с запятой в конце каждой строки JavaScript. Вообще-то символ ; не обязателен и необходим только тогда, когда в одной строке вводятся несколько операторов. Это чаще всего бывает в обработчиках событий, которые обсуждаются в Главе 10, "Обработка Событий".



Результат использования мнемоники JavaScript



Рисунок 9.2   Результат использования мнемоники JavaScript

Как и в HTML, после отображения вывода вид страницы может измениться только при её перезагрузке.

В отличие от обычных мнемоник, которые могут появляться в любом месте текстового потока HTML, мнемоники JavaScript интерпретируются только в правой части пары имя/значение HTML-атрибутов. Например, если у Вас имеется оператор:

<H4>&{myTitle};</H4>

он выведет строку myTitle вместо значения переменной myTitle.



Скрытие скриптов в тэгах комментария



Скрытие скриптов в тэгах комментария

Только версии Navigator'а 2.0 и более поздние распознают JavaScript. Чтобы другие браузеры игнорировали код JavaScript, поместите весь текст скрипта внутри тэгов комментария HTML и вставьте перед конечным тэгом комментария двойной слэш (//), являющийся символом однострочного комментария JavaScript:

<SCRIPT>
<!-- Начало скрытия содержимого скрипта от старых браузеров.
Операторы JavaScript...
// Здесь скрытие заканчивается. -->
</SCRIPT>

Поскольку браузеры обычно игнорируют неизвестные тэги, браузеры, не работающие с JavaScript, будут игнорировать начальный и конечный тэги SCRIPT. Все операторы скрипта находятся внутри тэгов HTML-комментария, поэтому они также игнорируются. Navigator правильно интерпретирует тэги SCRIPT и игнорирует строку скрипта, начинающуюся с двойного слэша (//).

Хотя Вы не обязаны использовать эту технику, это рекомендуется делать, чтобы Ваши страницы не генерировали неотформатированные операторы скрипта у пользователей, не использующих Navigator 2.0 и более поздние его версии.

ПРИМЕЧАНИЕ: Для упрощения, некоторые примеры в этой книге не прячут скрипты.



Специфицирование файла с кодом JavaScript


Атрибут SRC тэга <SCRIPT> даёт возможность специфицировать файл как источник кода JavaScript (вместо непосредственного встраивания JavaScript в HTML). Например:

<SCRIPT SRC="common.js">
</SCRIPT>

Этот атрибут часто используется для использования функций на нескольких страницах.

Закрывающий/конечный тэг </SCRIPT> необходим.

Операторы JavaScript в теле тэга <SCRIPT> с атрибутом SRC игнорируются, за исключением браузеров, не поддерживающих атрибут SRC, таких как Navigator 2.



Специфицирование версии JavaScript


Специфицирование версии JavaScript

Каждая версия Navigator'а поддерживает разные версии JavaScript. Для исключения проблем при использовании различных версий Navigator'а с JavaScript, используйте атрибут LANGUAGE в тэге <SCRIPT> для специфицирования версии JavaScript, для которой скрипт компилируется. Например, для использования синтаксиса JavaScript 1.2 Вы специфицируете:

<SCRIPT LANGUAGE="JavaScript1.2">

Используя атрибут LANGUAGE, Вы можете создавать скрипты для ранних версий Navigator'а. Если конкретный браузер не поддерживает специфицированную версию JavaScript, код игнорируется. Если Вы не специфицируете атрибут LANGUAGE, поведение по умолчанию зависит от версии Navigator'а.

В таблице перечислены тэги <SCRIPT>, поддерживаемые различными версиями Netscape Navigator'а.



Версии JavaScript и Navigator'а


Таблица 9.1 Версии JavaScript и Navigator'а

Версия Navigator'аВерсия JavaScript по умолчанию

Поддерживаемые Тэги <SCRIPT>

Navigator ранее 2.0JavaScript не поддерживаетсяОтсутствуют
Navigator 2.0JavaScript 1.0<SCRIPT LANGUAGE="JavaScript">
Navigator 3.0JavaScript 1.1<SCRIPT LANGUAGE="JavaScript1.1"> и все предыдущие версии
Navigator 4.0-4.05JavaScript 1.2<SCRIPT LANGUAGE="JavaScript1.2"> и все предыдущие версии
Navigator 4.06-4.5JavaScript 1.3<SCRIPT LANGUAGE="JavaScript1.3"> и все предыдущие версии

Navigator игнорирует код в тэгах <SCRIPT>, которые специфицируют неподдерживаемую версию. Например, Navigator 3.0 не поддерживает JavaScript 1.2, поэтому, если пользователь запускает скрипт JavaScript 1.2 в Navigator'е 3.0, этот скрипт будет проигнорирован.



Требования к файлам, специфицированным атрибутом SRC


Требования к файлам, специфицированным атрибутом SRC

Внешние файлы с кодом JavaScript не могут содержать никаких тэгов HTML: они обязаны содержать только операторы и определения функций JavaScript.

Внешние файлы JavaScript должны иметь расширение файла .js, и сервер обязан отображать расширение .js в MIME-тип application/x-javascript, который сервер отсылает обратно в шапке/header HTTP. Для отображения расширения в MIME-тип добавьте следующую строку в файл mime.types в директории config сервера, а затем перезапустите сервер.

type=application/x-javascriptexts=js

Если сервер не отображает .js в MIME-тип application/x-javascript, Navigator неправильно загружает файл JavaScript, специфицированный атрибутом SRC.

ПРИМЕЧАНИЕ: Это требование не применяется, если Вы используете локальные файлы.



URL, которые могут специфицироваться в атрибуте SRC



URL, которые могут специфицироваться в атрибуте SRC

В атрибуте SRC можно специфицировать любой URL, относительный или абсолютный. Например:

<SCRIPT SRC="http://home.netscape.com/functions/jsfuncs.js">

Если Вы загружаете документ любым URL, кроме file:, и этот документ содержит тэг <SCRIPT SRC="...">, этот внутренний атрибут SRC не может обратиться к другому URL file:.



Обработка Событий



Глава 10
Обработка Событий

Приложения JavaScript в браузере Navigator широко используют механизм обработки событий.

Events\События это акции, которые обычно возникают как результат некоторых действий пользователя. Например, щелчок по кнопке является событием, также как и изменение текста или перемещение мыши над гиперссылкой. Чтобы Ваш скрипт реагировал на события, Вы определяете event handlers\обработчики событий, такие как onChange и onClick.

В главе имеются следующие разделы:

Определение обработчика события

Объект Event

Захват событийПроверка данных, введённых в форму

Об обработке событий см. дополнительно статью Getting Ready for JavaScript 1.2 Events в онлайновом View Source magazine. Также JavaScript technical notes содержат информацию о программировании событий.

В таблице дано резюме по событиям JavaScript. О том, какие события поддерживаются конкретной версией JavaScript, см. книгу

Клиентский JavaScript.

Справочник.



Использование проверяющих функций



Использование проверяющих функций

В следующем примере тэг BODY документа использует qty_check как обработчик onChange для текстового поля и validateAndSubmit - как обработчик onClick для кнопки.

<BODY>
<FORM NAME="widget_order" ACTION="lwapp.html" METHOD="post">
How many widgets today?
<INPUT TYPE="text" NAME="quantity" onChange="qty_check(this, 0, 999)">
<BR>
<INPUT TYPE="button" VALUE="Enter Order" onClick="validateAndSubmit(this.form)">
</FORM>
</BODY>

Эта форма отправляет значения на страницу lwapp.html приложения серверного JavaScript. Можно также отправить форму CGI-программе. Форма показана на рисунке.



Объект Event


Каждое событие имеет ассоциированный объект event. Объект event предоставляет информацию о событии, такую как тип события и положение курсора в момент возникновения события. Когда событие возникает, и если обработчик был написан для обработки этого события, объект event отправляется обработчику как аргумент.

В случае с событием MouseDown, например, объект event содержит тип события ("MouseDown"), координаты x и y позиции курсора мыши в момент возникновения события, число - используемую кнопку мыши и поле, содержащее клавиши-модификаторы (Control, Alt, Meta или Shift), которые были нажаты в момент события. Свойства объекта event различаются для разных типов событий, как описано в книге

Клиентский JavaScript.

Справочник.

JavaScript 1.1 и предыдущие версии.

Объект event недоступен.



Определение обработчика события


Вы определяете обработчик события (функцию или серию операторов JavaScript) для обработки события. Если событие применяется к HTML-тэгу (то есть событие применяется к JavaScript-объекту, созданному из этого тэга), то Вы можете определить для него обработчик события. Имя обработчика это имя события с префиксом "on." Например, обработчик для события focus называется onFocus.

Для создания обработчика события для тэга HTML добавьте атрибут обработчика события в этот тэг. Поместите код JavaScript в кавычки как значение атрибута. Общий синтаксис таков:

<TAG eventHandler="JavaScript Code">

где TAG это тэг HTML, eventHandler это имя обработчика события, а JavaScript Code это последовательность операторов JavaScript.

Например, Вы создали JavaScript-функцию compute. Вы выполняете вызов Navigator'ом этой функции, когда пользователь щёлкает кнопку, назначив вызов функции обработчику onClick кнопки:

<INPUT TYPE="button" VALUE="Calculate" onClick="compute(this.form)">

Вы можете указать любые операторы JavaScript в качестве значения атрибута onClick. Эти операторы выполняются, если пользователь щёлкает по кнопке. При включении нескольких операторов разделяйте их символом (;).

В предыдущем примере this.form ссылается на текущую форму. Ключевое слово this ссылается на текущий объект, которым в данном случае является кнопка. Конструкция this.form, следовательно, ссылается на форму, содержащую кнопку. Обработчик onClick это вызов функции compute с текущей формой в качестве аргумента.

Если Вы создаёте обработчик события, соответствующий объект JavaScript получает свойство с именем обработчика события. Это свойство даёт Вам доступ к обработчику события данного объекта. Например, в предыдущем примере JavaScript создаёт объект Button со свойством onclick, значением которого является "compute(this.form)".

Не забывайте чередовать двойные и одинарные кавычки. Поскольку обработчик события в HTML обязан быть заключён в кавычки, Вы обязаны использовать одинарные кавычки как ограничители строковых аргументов. Например:

<INPUT TYPE="button" NAME="Button1" VALUE="Open Sesame!"
onClick="window.open('mydoc.html', 'newWin')">

Вообще хорошей практикой может стать определение функции для обработчика события вместо использования нескольких операторов JavaScript:

это сделает Ваш код более модульным - Вы сможете использовать ту же самую функцию как обработчик события для различных элементов.это сделает Ваш код более понятным.


Определение Обработчика События

Далее определяется функция, обрабатывающая событие. Аргумент e это event-объект события.

function clickHandler(e) {

//Здесь идёт обработка события.
   //Процесс описан ниже.
}

У Вас имеются следующие опции для обработки события:

Возвращать true. В гиперссылке выполняется переход, и никакой обработчик не отмечается. Если событие не может быть отменено, это прекращает обработку данного события.   function clickHandler(e) {
      return true;
   }

Это даёт возможность полностью обработать событие документом или окном. Событие не обрабатывается каким-либо иным объектом, таким как button в document или дочерний фрэйм в window.

Возвращать false. В гиперссылке - переход по ссылке не выполняется. Если событие не может быть отменено, это прекращает обработку данного события.   function clickHandler(e) {
      return false;
   }

Это позволяет подавить обработку событий данного типа. Событие не обрабатывается каким-либо другим объектом, таким как кнопка в документе или дочерний фрэйм в окне. Вы может использовать это, например, для подавления событий от правой кнопки мыши в Вашем приложении.

Вызывать routeEvent. JavaScript ищет другие обработчики для данного события. Если другой объект пытается захватить событие (такой объект как document), JavaScript вызывает его обработчик события. Если другой объект не пытается захватить событие, JavaScript ищет изначальный целевой обработчик объекта (такого как объект button). Функция routeEvent возвращает значение, возвращаемое обработчиком события. Захватывающий объект может определить способ продолжения работы на основе возвращённого результата.

Если routeEvent вызывает обработчик события, этот обработчик активируется. Если routeEvent вызывает обработчик, функцией которого является отображение новой страницы, эта акция выполняется без возвращения к вызывающему объекту.   function clickHandler(e) {
      var retval = routeEvent(e);
      if (retval == false) return false;
      else return true;
   }

Вызвать метод handleEvent получателя события. Любой объект, который может регистрировать обработчики событий, является получателем события/event receiver. Это метод явным образом вызывает обработчик получателя события в обход иерархии захвата. Например, если Вы хотите, чтобы все события Click направлялись первой гиперссылке страницы, Вы можете записать:   function clickHandler(e) {
      window.document.links[0].handleEvent(e);
   }

Пока ссылка имеет обработчик onClick, эта ссылка будет обрабатывать любые click-события, которые она получает.



Полный пример


Полный пример

В этом примере window и document захватывают и освобождают события:

<HTML>
<SCRIPT>function fun1(e) {
alert ("The window got an event of type: " + e.type +
      " and will call routeEvent.");
   window.routeEvent(e);
   alert ("The window returned from routeEvent.");
   return true;
}function fun2(e) {
   alert ("The document got an event of type: " + e.type);
   return false;
}function setWindowCapture() {
   window.captureEvents(Event.CLICK);
}function releaseWindowCapture() {
   window.releaseEvents(Event.CLICK);
}function setDocCapture() {
   document.captureEvents(Event.CLICK);
}function releaseDocCapture() {
   document.releaseEvents(Event.CLICK);
}window.onclick=fun1;
document.onclick=fun2;</SCRIPT>
...
</HTML>



использование обработчика события



Пример: использование обработчика события

В форме, показанной на следующем рисунке, Вы можете ввести выражение (например, 2+2) в первое текстовое поле, а затем щёлкнуть кнопку. Во втором поле будет выведено значение выражения (в данном случае, 4).



Пример проверяющих функций



Пример проверяющих функций

Вот примеры простых проверяющих функций:

<HEAD>
<SCRIPT LANGUAGE="JavaScript">
function isaPosNum(s) {
return (parseInt(s) > 0)
}function qty_check(item, min, max) {
   var returnVal = false
   if (!isaPosNum(item.value))
      alert("Please enter a positive number")
   else if (parseInt(item.value) < min)
      alert("Please enter a " + item.name + " greater than " + min)
   else if (parseInt(item.value) > max)
      alert("Please enter a " + item.name + " less than " + max)
   else
      returnVal = true
   return returnVal
}function validateAndSubmit(theform) {
   if (qty_check(theform.quantity, 0, 999)) {
      alert("Order has been Submitted")
      return true
   }
   else {
      alert("Sorry, Order Cannot Be Submitted!")
      return false
   }
}
</SCRIPT>
</HEAD>

isaPosNum это простая функция, которая возвращает true, если её аргумент - положительное число, и false - в ином случае.

qty_check принимает три аргумента: объект, соответствующий проверяемому элементу формы (item), и минимальное и максимальное допустимые значения для item (min и max). Она проверяет, является ли item числом в диапазоне от min до max, и выводит диалог alert, если это не так.

validateAndSubmit принимает в качестве аргумента Form-объект; она использует qty_check для проверки значения элемента формы и отправляет форму, если ввод верен. В ином случае она выводит окно alert и не отправляет форму.



Проверка данных, введённых в форму


Важным является использование JavaScript для проверки введённых в форму данных, предназначенных для последующей обработки серверными программами, такими как приложения серверного JavaScript или CGI-программы. Эта проверка делается по следующими причинам:

Уменьшается нагрузка на сервер. "Плохие" данные фильтруются уже перед отправкой данных на сервер.

Уменьшается задержка в случае ошибки пользователя. Иначе проверка выполняется на сервере, и данные обязаны путешествовать с клиента на сервер, для обработки, и обратно клиенту - для повторения ввода.Упрощаются серверные программы.

В общем, Вам понадобится проверять ввод как минимум в двух случаях:

После ввода данных пользователем - обработчиком onChange в каждом элементе формы, который Вам нужно проверить.

Если пользователь отправляет форму - обработчиком onClick submits-кнопки, отправляющей форму.

Страница JavaScript на сайте DevEdge содержит ссылки на примеры кода. Одна из этих ссылок - это полный набор функций для проверки данных формы.
В следующем разделе даны некоторые простые примеры, но посмотрите также примеры на сайте DevEdge.



Регистрация обработчика события


Регистрация обработчика события

Наконец, функция регистрируется как обработчик данного события в окне:

window.onClick = clickHandler;



Форма с обработчиком события



Рисунок 10.1   Форма с обработчиком события

Скрипт для этой формы таков:

<HEAD>
<SCRIPT>
<!-- Скрываем от старых браузеров
function compute(f) {
   if (confirm("Are уou sure?"))
      f.result.value = eval(f.expr.value)
   else
      alert("Please come back again.")
}
// конец скрытия -->
</SCRIPT>
</HEAD><BODY>
<FORM>
Enter an expression:
<INPUT TYPE="text" NAME="expr" SIZE=15 >
<INPUT TYPE="button" VALUE="Calculate" onClick="compute(this.form)">
<BR>
Result:
<INPUT TYPE="text" NAME="result" SIZE=15 >
</FORM>
</BODY>

HEAD/шапка документа определяет функцию compute, принимающую один аргумент, f, который является Form-объектом. Эта функция использует метод window.confirm для отображения диалога Confirm с кнопками OK и Cancel.

Если пользователь щёлкает OK, confirm возвращает true, а значением текстового поля result становится значение eval(f.expr.value). Функция JavaScript eval вычисляет свой аргумент, который может быть любой строкой, представляющей любые выражение или операторы JavaScript.

Если пользователь нажал Cancel, confirm возвращает false, и метод alert выводит другое сообщение.

На форме имеется кнопка с обработчиком onClick, в котором вызывается функция compute. Когда пользователь щёлкает кнопку, JavaScript вызывает compute с аргументом this.form, который означает текущий Form-объект. В compute на this.form делается ссылка как на аргумент f.



Обработчики Событий JavaScript



Таблица 10.1  Обработчики Событий JavaScript

Событие

Применяется кВозникает, когда

Обработчик

Abortизображениямпользователь прерывает загрузку изображения (например, щёлкну гиперссылку или кнопку Stop)onAbort
Blurокнам и всем элементам формыпользователь убирает фокус ввода с окна или элемента формыonBlur
Changeтекстовым полям, спискам selectпользователь изменяет значение элементаonChange
Clickкнопкам, radio-кнопкам, переключателям/checkboxes, кнопкам submit и reset, гиперссылкампользователь щёлкает по элементу формы или кнопкеonClick
DragDropокнампользователь "отпускает" мышью объект в окне браузера, как при перемещении файловonDragDrop
Errorизображениям, окнамзагрузка документа или изображения вызвала ошибкуonError
Focusокнам и всем элементам формыпользователь передаёт фокус окну или элементу формыonFocus
KeyDownдокументам, изображениям, гиперссылкам, текстовым полямпользователь нажал клавишу клавиатурыonKeyDown
KeyPressдокументам, изображениям, гиперссылкам, текстовым полямпользователь нажал или (?) удерживает нажатой клавишу клавиатурыonKeyPress
KeyUpдокументам, изображениям, гиперссылкам, текстовым полямпользователь отпустил клавишу клавиатурыonKeyUp
Loadтелу документапользователь загружает страницу в NavigatoronLoad
MouseDownдокументам, кнопкам, гиперссылкампользователь нажал клавишу мышиonMouseDown
MouseMoveпо умолчанию - ни к чемупользователь перемещает курсорonMouseMove
MouseOutобластям, гиперссылкампользователь перемещает курсор за пределы клиентской карты изображений или гиперссылкиonMouseOut
MouseOverгиперссылкампользователь перемещает курсор над гиперссылкойonMouseOver
MouseUpдокументам, кнопкам, гиперссылкампользователь отпускает клавишу мышиonMouseUp
Moveокнампользователь или скрипт перемещает окноonMove
Resetформампользователь восстанавливает начальные значения формы (щёлкает кнопку Reset)onReset
Resizeокнампользователь или скрипт изменяет размер окнаonResize
Selectтекстовым полямпользователь выделяет поле ввода элемента формыonSelect
Submitформампользователь отправляет формуonSubmit
Unloadтелу документапользователь покидает страницуonUnload



Включение захвата событий



Включение захвата событий

Для настройки захвата окном всех событий Click используйте примерно такой оператор:

window.captureEvents(Event.CLICK);

Аргументом свойства captureEvents объекта event является тип захватываемого события. Для захвата нескольких типов событий, аргументом является список с разделением символом (|). Например, следующий оператор захватывает события Click, MouseDown и MouseUp:

window.captureEvents(Event.CLICK | Event.MOUSEDOWN | Event.MOUSEUP)

ПРИМЕЧАНИЕ:

Если окно с фрэймами должно захватывать события на страницах, загружаемых с различных серверов, Вам необходимо использовать captureEvents в маркированном скрипте и вызывать enableExternalCapture. О маркированных скриптах см. Главу 14 "Безопасность в JavaScript".



Вызов обработчиков событий явным образом


Вызов обработчиков событий явным образом

Руководствуйтесь следующими указаниями при вызове обработчиков событий.

Вы можете устанавливать обработчик, специфицированный в HTML, как показано здесь в примерах.<SCRIPT LANGUAGE="JavaScript">
function fun1() {
...
}
function fun2() {
   ...
}
</SCRIPT><FORM NAME="myForm">
<INPUT TYPE="button" NAME="myButton"
   onClick="fun1()">
</FORM><SCRIPT>
document.myForm.myButton.onclick=fun2
</SCRIPT>

JavaScript 1.0. Вы не можете устанавливать обработчик события.

Обработчики событий являются ссылками на функции, поэтому Вы обязаны присвоить fun2 самой себе, а не fun2() (второй вариант вызывает fun2 и содержит любые тип и значение, возвращаемые функцией fun2).

Поскольку HTML-атрибуты обработчика события являются литеральными телами функций, Вы не можете использовать <INPUT onClick=fun1> в HTML, чтобы назначить fun1 обработчику onClick. Вместо этого, Вы обязаны установить значение в JavaScript, как в предыдущем примере.

JavaScript 1.1 и предыдущие версии.

Вы обязаны вводить имена обработчиков символами нижнего регистра, например, myForm.onsubmit или myButton.onclick.



Захват событий


Обычно событие обрабатывается объектом, в котором это событие возникает. Например, если пользователь щёлкает по кнопке, вызывается обработчик этого события. Иногда Вам может понадобиться, чтобы объект window или document обрабатывал определённые события. Например, Вам может понадобиться, чтобы объект document обрабатывал все события MouseDown вне зависимости от того, в каком месте документа они возникают.

Модель захвата событий JavaScript даёт возможность определять методы, которые захватывают и обрабатывают события, до того как они достигнут своей предполагаемой цели. Для этого объекты window, document и layer используют следующие методы:

captureEvents - захватывает события специфицированного типа.

releaseEvents - игнорирует захват событий специфицированного типа.

routeEvent - перенаправляет захваченное событие специфицированному объекту.

handleEvent - обрабатывает захваченное событие (это не метод объекта layer).

JavaScript1.1 и предыдущие версии. Захват событий невозможен.

Как пример, предположим, Вы хотите захватить все события Click, возникающие в окне. В сжатом виде, шаги по захвату событий таковы:

Включить захват событий

Определить обработчик события

Зарегистрировать обработчик события

В последующих разделах эти шаги поясняются.