Глава 3.7. Использование объектов

3.7.1. Объекты, свойства и методы

3.7.1.1. Основные понятия

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

  • встроенные объекты исполняющей системы;
  • объекты среды, в которой исполняется сценарий (т. е. либо объекты клиента, либо объекты сервера);
  • пользовательские объекты, создаваемые сценарием в процессе его выполнения.

Объект JavaScript — это неупорядоченный набор свойств. Свойство, являющееся функцией, называется методом. Для доступа к свойству объекта используется синтаксис:

имя_объекта.имя_свойства

Если название свойства задано текстовой строкой, то доступ к свойству возможен и так:

имя_объекта["имя_свойства"]

Этот синтаксис используется оператором итерации for…in.

Каждое свойство состоит из названия, значения и набора следующих атрибутов:

Атрибут Описание
DontEnum Свойство не должно попадать в перечисление при итерации объекта оператором for…in.
DontDelete Попытка программно удалить данное свойство будет проигнорирована. См. описание операции delete.
ReadOnly Неизменяемое свойство. Попытка программно изменить данное свойство будет проигнорирована. (Отметим, что в некоторых случаях значение свойства с этим атрибутом может быть изменено через внешнюю среду.)

Новое свойство объекта создается просто присваиванием ему значения. Пусть, например, мы уже создали объект myBrowser, который должен описывать наш Веб-обозреватель. Это описание будет состоять из названия обозревателя (name) и его версии (version). Для создания указанных свойств мы должны включить в сценарий следующие строки:

myBrowser.name = "Microsoft Internet Explorer";
myBrowser.version = "5.5";

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

document.write(myBrowser.name, myBrowser.version);

Существует два способа создания новых объектов в JavaScript, а именно:

  1. Использование инициализатора объекта.
  2. Использование конструктора объектов.

3.7.1.2. Создание объектов с помощью инициализатора

Этот способ позволяет одновременно создать объект и присвоить значения всем или части его свойств. Он применяется в тех случаях, когда мы создаем объект с уникальным набором свойств. Инициализатор объекта имеет вид:

{свойство:значение [,свойство:значение]?}

Здесь свойство — идентификатор, задающий имя свойства, а значение — выражение, задающее значение этого свойства.

Например, объект myBrowser из предыдущего примера может быть создан так:

var myBrowser = {name: "Microsoft Internet Explorer", version: "5.5"};

Усложним этот пример, добавив еще одно свойство объекта myBrowser, которое называется options (опции обозревателя) и само является объектом:

var myBrowser = {name: "Microsoft Internet Explorer", version: "5.5",
  options: {enableJava: true, enableCookies: false}};

3.7.1.3. Создание объектов с помощью конструктора

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

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

Например, конструктор для класса объектов Browser из предыдущего примера может иметь следующий вид:

function Browser(name, version) {
  this.name = name;
  this.version = version;
}

Обратите внимание на использование операции this для доступа к свойствам объекта.

Теперь для создания новых объектов класса Browser достаточно вызвать этот конструктор в операции new, например:

var myBrowser = new Browser("Microsoft Internet Explorer", "5.5");

Вспомним теперь, что выше мы добавили свойство options объекта Browser, которое само является объектом. Перепишем приведенный пример с учетом этого свойства:

function Options(enableJava, enableCookies) {
  this.enableJava = enableJava;
  this.enableCookies = enableCookies;
}

function Browser(name, version) {
  this.name = name;
  this.version = version;
  this.options = options;
}

var myOptions = new Options(true, false);
var myBrowser = new Browser("Microsoft Internet Explorer", "5.5", myOptions);

Для доступа к свойствам свойства options используется нотация myBrowser.options.enableJava.

3.7.1.4. Создание методов

Поскольку методы являются разновидностью свойств, они создаются так же, как описано выше. Например, мы можем добавить к конструктору объектов Browser метод aboutBrowser, который будет выводить на экран обозревателя информацию о свойствах этого объекта:

function showBrowser() {
  document.write("Обозреватель: " + this.name + " " + this.version);
}

function Browser(name, version) {
  this.name = name;
  this.version = version;
  this.aboutBrowser = showBrowser;
}

В дальнейшем мы можем вызвать этот метод так: myBrowser.aboutBrowser().

При желании конструктор можно записать и короче, используя вложенное определение функции:

function Browser(name, version) {
  this.name = name;
  this.version = version;
  this.aboutBrowser = function() {
    document.write("Обозреватель: " + this.name + " " + this.version);
  }
}

3.7.1.5. Изменение прототипа объекта

Допустим, что мы хотим в процессе выполнения сценария добавить новое свойство security (безопасность) классу объектов Options (подчеркнем еще раз — классу объектов, а не отдельному его представителю myOptions). Для этого используется свойство prototype объекта Function:

Options.prototype.security = null;

Теперь мы можем присвоить значение новому свойству объекта:

myBrowser.options.security = "Высокая";

Для удаления свойств объектов используется операция delete, например:

delete Options.prototype.security;

JavaScript позволяет нам задать новый прототип для класса пользовательских объектов (прототипы встроенных объектов доступны только для чтения). Рассмотрим такой пример:

function Circle(radius) {
  this.radius = radius;
}

Circle.prototype.area = function() {
  return Math.PI * this.radius * this.radius;
}

function FullCircle(x, y, radius) {
  this.x = x;
  this.y = y;
  this.radius = radius;
}

FullCircle.prototype = Circle.prototype;

var myCircle = new FullCircle(0, 0, 1);
document.write(myCircle.area());

В этом примере сначала определяется класс объектов Circle со свойством radius и методом area, возвращающим площадь круга. Затем определяется класс FullCircle, конструктор которого дополнительно содержит координаты центра окружности. Затем указывается, что он наследует прототип класс Circle. После этого мы создаем объект myCircle и вызываем его метод area, который он унаследовал от прототипа класса Circle.

3.7.1.6. Удаление объектов

Мы можем удалить ранее созданный объект с помощью операции delete, например:

delete myBrowser;

3.7.2. Объектная модель JavaScript

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

3.7.2.1. Прототипы объектов

Большинство объектно-ориентированных языков (например, Java и C++) основаны на двух базовых понятиях: классы объектов и экземпляры (instances) объектов.

  • Класс объектов — это абстрактное понятие, описывающее все свойства данного класса (в Java эти свойства называются полями и методами, а в C++ членами класса, но суть от этого не меняется).
  • Экземпляр объекта — это реализация класса, т. е. конкретный объект, наделенный всеми свойствами данного класса.

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

3.7.2.2. Создание объектов

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

Создание объектов в JavaScript происходит примерно так же, но здесь декларация конструктора совпадает с декларацией класса. Иными словами, мы определяем конструктор как функцию, которая создает объекты с заданным начальным набором свойств и их значений. Затем мы так же создаем объекты вызовом операции  new имя_конструктора(...).

3.7.2.3. Наследование

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

JavaScript поддерживает наследование, основанное на прототипах. С каждым конструктором связан соответствующий прототип объекта, и каждый объект, созданный конструктором, содержит неявную ссылку на этот прототип. Прототип, в свою очередь, может содержать ссылку на свой прототип и так далее. Так образуется цепочка прототипов. Ссылка на свойство объекта — это ссылка на первый прототип в цепочке прототипов объекта, который содержит свойство с данным именем. Иными словами, если данный объект имеет свойство с данным именем, то используется ссылка на это свойство; если нет, то исследуется прототип этого объекта и т. д.

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

3.7.2.4. Резюме

Перечисленные в этом разделе отличия объектной модели JavaScript от языков, основанных на классах, сведены в следующей таблице.

Таблица 3.8. Объектная модель JavaScript
Модель, основанная на классах
(Java и C++)
Модель, основанная на прототипах (JavaScript)
Класс объектов и экземпляр объекта — это различные понятия. Все объекты являются экземплярами объектов.
Класс определяется декларацией класса. Экземпляр класса создается конструктором. Набор объектов определяется и создается функцией-конструктором.
Новый объект создается операцией new. Новый объект создается операцией new.
Существует иерархия классов, в которой новые классы являются потомками ранее определенных. Существует иерархия объектов, в которой объект имеет прототип, заданной функцией-конструктором.
Свойства наследуются по цепочке классов-потомков. Свойства наследуются по цепочке прототипов.
Декларация класса определяет все свойства всех экземпляров данного класса. Набор свойств не может динамически изменяться в ходе выполнения программы. Конструктор или прототип определяют начальный набор свойств. Свойства набора объектов или отдельного объекта могут динамически добавляться и удаляться в ходе выполнения программы.