Работа с объектами

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

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

Обзор объектов

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

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

Объекты и свойства

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

objectName.propertyName

Как и все переменные JavaScript, оба имя объекта (которое тоже может быть переменной) и имя свойства являются чуствительными к регистру. Вы определяете свойство просто указывая его значение. Например, давайте созданим объект myCar и определим его свойства make, model, и year след.образом:

var myCar = new Object();
myCar.make = "Ford";
myCar.model = "Mustang";
myCar.year = 1969;

Доступ к свойствам объектов JavaScript можно получить через квадратные скобки. Объекты являются, ассоциативными массивами, так как каждое свойство ассоциировано с именем, через которое можно получить доступ к нему. Например, доступ к свойствам объекта myCar можно получить след.образом:

myCar["make"] = "Ford";
myCar["model"] = "Mustang";
myCar["year"] = 1969;

Имена свойств объекта могут быть валидными строками JavaScript, или тем, что может быть сконвертировано в строку, включая пустую строку. Как бы там ни было, доступ к любому имени свойства, которое содержит невалидный JavaScript идентификатор (например имя свойства содержит в себе пробел и тире, или начинается с цифры) может быть получен с использование квадратных скобок. Этот способ также полезен в случае если имена свойств определяются динамически в процессе выполнения скрипта. Примеры далее:

var myObj = new Object(),
    str = "myString",
    rand = Math.random(),
    obj = new Object();

myObj.type              = "Dot syntax";
myObj["date created"]   = "String with space";
myObj[str]              = "String value";
myObj[rand]             = "Random Number";
myObj[obj]              = "Object";
myObj[""]               = "Even an empty string";

console.log(myObj);

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

var propertyName = "make";
myCar[propertyName] = "Ford";

propertyName = "model";
myCar[propertyName] = "Mustang";

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

function showProps(obj, objName) {
  var result = "";
  for (var i in obj) {
    if (obj.hasOwnProperty(i)) {
        result += objName + "." + i + " = " + obj[i] + "\n";
    }
  }
  return result;
}

Так что, если вызвать эту функцию вот так showProps(myCar, "myCar") то получим результат:

myCar.make = Ford
myCar.model = Mustang
myCar.year = 1969

Все в JavaScript это объект

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

Перечисление всех свойств объекта

Начиная с ECMAScript 5, есть три способа перечислить все свойства (или получить список из онных) объекта:

  • циклы for...in
    Этот медот перебирает все перечисляемые свойства объекта и его цепочки прототипов
  • Object.keys(o)
    Этот метод возвращает массив со всеми собственными (те что цепочке прототипов не войдут в массив) именами перечисляемых свойств объекта o.
  • Object.getOwnPropertyNames(o)
    Этот метод возвращает массив содержащий все имена своих свойств (перечисляемых и не) объекта o.

В ECMAScript 5 нет родного способа перечислить сами свойства объекта. Но это можно сделать с помощью след. функции:

function listAllProperties(o){     
	var objectToInspect;     
	var result = [];
	
	for(objectToInspect = o; objectToInspect !== null; objectToInspect = Object.getPrototypeOf(objectToInspect)){  
		result = result.concat(Object.getOwnPropertyNames(objectToInspect));  
	}
	
	return result; 
}

Это может быть полезно для обнаружения скрытых (hidden) свойств (свойства в цепочке прототипа, которые недоступны через объект, так как другое свойство имеет такое имя в предыдущем звене из цепочки прототипа). Перечислить доступные свойства можно, если удалить дубликаты из массива.

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

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

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

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

var obj = { property_1:   value_1,   // property_# may be an identifier...
            2:            value_2,   // or a number...
            // ...,
            "property n": value_n }; // or a string

здесь obj это имя нового объекта, каждое property_i это идентификатор (имя, число или строковый литерал), и каждый value_i это значения, назначенные property_i. Имя obj и ссылка объекта на него необязательна; если далее вам не надо будет ссылаться на данный объект, то вам не обязательно назначать объект переменной. (Обратите внимание, что вам потребуется обернуть литерал объекта в скобки, если объект находится в месте, где ожидается инструкция, чтобы интерпретатор не перепутал его с блоком.)

Если объект создан при помощи инициализатора объектов на высшем уровне скрипта, то JavaScript интерпретирует объект каждый раз, когда анализирует выражение, содержащее объект записанный как литерал. Плюс, если пользоваться функцией инициализатором, то он будет создается каждый раз, когда функция вызывается.

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

if (cond) var x = {hi: "there"};

След. пример создает объект myHonda с тремя свойствами. Заметьте, что свойство engine - это также объект со своими собственными свойствами.

var myHonda = {
  color: "red",
  wheels: 4,
  engine: {
    cylinders: 4,
    size: 2.2
  }
};

Вы также можете использовать инициализатор объекта для создания массивов. Смотрите array literals.

До JavaScript 1.1 не было возможности пользоваться инициализаторами объекта. Единственный способ создавать объекты -- это пользоваться функциями-конструкторами или функциями других объектов, предназначенных для этой цели. Смотрите Using a constructor function.

Использованиии функции конструктора

Другой способ создать объект в два шага описан ниже:

  1. Определите объект, набрав функцию-конструктор. Название такой функции, как правило, начинается с заглавной буквы.
  2. Создайте экземпляр объекта с помощью ключевого слова new.

Чтобы определить тип объекта, создайте функцию для которая определяет тип объекта, его имя, свойства и методы. Наример положим вы хотите создать тип объект для cars.Вы хотите чтобы объект этого типа назывался car, и вы хотите чтобы у него были свойства make, model, и year. Чтобы сделать это, напишите след. функцию:

function Car(make, model, year) {
  this.make = make;
  this.model = model;
  this.year = year;
}

Заметьте, что используется this чтобы назначить значения (переданные как аргументы функции) свойствам объекта.

Теперь вы можете создать объект нызваемый mycar след. образом:

var mycar = new Car("Eagle", "Talon TSi", 1993);

Эта инструкция создает объект типа Car с ссылкой mycar и назначает определенные значения его свойствам. Значение mycar.make строка "Eagle", mycar.year это целое число 1993, и так далее.

Вы можете создать столько объектов car сколько нужно просто вызывая  new. Например,

var kenscar = new Car("Nissan", "300ZX", 1992);
var vpgscar = new Car("Mazda", "Miata", 1990);

Объект может иметь свойтво которое будет другим объектом. Например, далее определяет объект типа Person след. образом:

function Person(name, age, sex) {
  this.name = name;
  this.age = age;
  this.sex = sex;
}

и затем создать два новых экземпляря объектов person как показано далее:

var rand = new Person("Rand McKinnon", 33, "M");
var ken = new Person("Ken Jones", 39, "M");

Затем, вы можете переписать определение car и включить в него свойство owner которому назначить объект person след. образом:

function Car(make, model, year, owner) {
  this.make = make;
  this.model = model;
  this.year = year;
  this.owner = owner;
}

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

var car1 = new Car("Eagle", "Talon TSi", 1993, rand);
var car2 = new Car("Nissan", "300ZX", 1992, ken);

Замечание. Вместо того чтобы передавать строку литерал или целое число когда создаются новые объекты, в выражениях выше передаются объекты rand и ken как аргумент функции. Затем если вам нужно узнать имя владельца car2, это можно узнать след. образом:

car2.owner.name

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

car1.color = "black";

добавляет свойство color к car1, и устанавливаего его значение равным "black." Как бы там ни было, это не влияет на любые другие объекты. Чтобы добавить новое свойство всем объектам одного типа, вы должны добавить свойство в определения типа объекта car.

Использование метода Object.create

Объекты также можно создавать с помощью метода Object.create. Этот метод очень удобный, так как позволяет вам указывать объект прототип для нового вашего объекта, без определения функции конструктора. Больше информации об этом методе и том как им пользоваться, смотрите Object.create method

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

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

Индексы свойств объекта

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

Это ограничение налагается когда вы создаете объект и его свойства с помощью функции конструктора (как мы это делали ранее с типом Car ) и когда вы определяете индивидуальные свойства явно (например, myCar.color = "red"). Если вы изначально определили свойство объекта через индекс, например myCar[5] = "25 mpg", то впоследствии сослаться на это свойство можно только так myCar[5].

Исключение из правил объекты из HTML, например массив forms. Вы всегда можете сослаться на объекты в этих массивах или используя их индекс (который основывается на порядке появления в HTML документе) или на их именах (если таковые были определены). Например, если второй html тег <FORM> в документе имеет значение аттрибута NAME равное "myForm", вы можете сослаться на эту форму вот так document.forms[1] или document.forms["myForm"] или document.myForm.

Определение свойств для типа объекта

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

Car.prototype.color = null;
car1.color = "black";

Смотри prototype property объекта Function в Справочнике JavaScript для получения деталей.

Определение методов

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

objectName.methodname = function_name;

var myObj = {
  myMethod: function(params) {
    // ...do something
  }
};

где objectName это существующий объект, methodname это имя которое вы присваиваете методу, и function_name это имя самой функции.

Затме вы можете вызвать метод в контексте объекта след. образом:

object.methodname(params);

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

function displayCar() {
  var result = "A Beautiful " + this.year + " " + this.make
    + " " + this.model;
  pretty_print(result);
}

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

Вы можете сделать эту функцию методом car добавив инструкцию

this.displayCar = displayCar;

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

function Car(make, model, year, owner) {
  this.make = make;
  this.model = model;
  this.year = year;
  this.owner = owner;
  this.displayCar = displayCar;
}

Затем вы можете вызвать метод displayCar для каждого из объектов след. образом:

car1.displayCar();
car2.displayCar();

Вывод этих выражений показан на рисунке ниже.

Image:obja.gif

Figure 7.1: Displaying method output.

Использование this для ссылки на объект

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

function validate(obj, lowval, hival) {
  if ((obj.value < lowval) || (obj.value > hival))
    alert("Invalid Value!");
}

Когда вы вызываете validate в каждой из форм в обработчике события onchange, используйте this чтобы передать этот элемент, как это сделано ниже:

<input type="text" name="age" size="3"
  onChange="validate(this, 18, 99)">

В общем, this ссылается на объект вызвавший метод.

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

<form name="myForm">
<p><label>Form name:<input type="text" name="text1" value="Beluga"></label>
<p><input name="button1" type="button" value="Show Form Name"
     onclick="this.form.text1.value = this.form.name">
</p>
</form>

Определение геттеров и сеттеров

Геттер (от англ. get получить)  это метод которые получает значениме определенного свойства. Сеттер (от англ. set - присвоить) это метод который присваивает значение определенному свойству объекта. Вы можете определить геттеры и сеттеры для любых из встроенных или вами определенных объектов которые поддерживают добавление новых свойств. Синтаксис определения геттеров и сеттеров пользуется синтаксисом литеральных объектов.

JavaScript 1.8.1 note

Starting in JavaScript 1.8.1, setters are no longer called when setting properties in object and array initializers.

Следующая сессия в JS шеле иллюстрирует как геттеры и сеттеры работают с объектом o определенным пользователем. JS шелл это приложение которое позволяет разработчику тестировать код JavaScript интерактивно или выполнить группу инструкций.

js> var o = {a: 7, get b() {return this.a + 1;}, set c(x) {this.a = x / 2}};
[object Object]
js> o.a;
7
js> o.b;
8
js> o.c = 50;
js> o.a;
25

Свойства объекта o это:

  • o.a — число
  • o.b — геттер который возвращает o.a плюс 1
  • o.c — сеттер который присваивает значение o.a половине значения которое передано o.c

Заметьте, что имена функций геттера и сеттера определны в литерале объекта используя "[gs]et property()" (в отличии от __define[GS]etter__ ниже)  не являются именами самих геттеров, хотя синтаксис [gs]et propertyName(){ } может ввести вас в заблуждение. Для именования функции геттера или сеттера использущьх синтаксис "[gs]et property()", определите явно имена функций программно, используя Object.defineProperty (или Object.prototype.__defineGetter__ при наследовании).

Данная JavaScript shell сессия показывает как геттер и сеттер  могут расширить прототип класса Date добавлением свойства year  во все экземпляры переопределеннго класса Date. Используются существующие в классе Date методы getFullYear и setFullYear для добавления геттера и сеттера свойства year.

Данный код определяет геттер и сеттер для свойства year:

js> var d = Date.prototype;
js> d.__defineGetter__("year", function() { return this.getFullYear(); });
js> d.__defineSetter__("year", function(y) { this.setFullYear(y); });

Данный код использует геттер и сеттер объекта Date:

js> var now = new Date;
js> print(now.year);
2000
js> now.year = 2001;
987617605170
js> print(now);
Wed Apr 18 11:13:25 GMT-0700 (Pacific Daylight Time) 2001

Устаревший синтаксис

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

Заключение

В принципе, геттеры и сеттеры могут быть:

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

Когда геттеры и сеттеры определяются с использованием инициализаторов объекта всё что вам нужно сделать, добвить префекс get для геттера и префикс set для сеттера. И конечно, геттер не должен ожидать получения параметров, в то время как сеттер получает ровно один параметр (для установки новго значения). Например:

var o = {
  a: 7,
  get b() { return this.a + 1; },
  set c(x) { this.a = x / 2; }
};

Геттеры и сеттеры могут быть добавлены к объекту в любое время после его создания с помощью метода Object.defineProperties. Этот метод первым параметром принимает объект, которому требуется назначить геттер и/или сеттер, а вторым объект, содержащий методы геттера и/или сеттера. См.  ниже пример определения некоторых геттера и сеттера использованных так же в предыдущем примере:

var o = { a:0 };
Object.defineProperties(o, {
    "b": { get: function () { return this.a + 1; } },
    "c": { set: function (x) { this.a = x / 2; } }
});

o.c = 10 // Вызывает сеттер, который присваивает 10 / 2 свойству a
console.log(o.b) // Вызывает геттер, который возвращает a + 1 (6)
 
 
 
 
 
 
 
 
 
 

Того же можно добиться с помощью двух специальных методов: __defineGetter__,  __defineSetter__. Каждый из этих методов принимает два параметра: имя свойства и функция вызываемая геттером / сеттером. См пример ниже (продолжение предыдущего примера):

o.__defineGetter__("b", function() { return this.a + 1; });
o.__defineSetter__("c", function(x) { this.a = x / 2; });

Однако, эти методы устарели и не рекомендуются к дальнейшему использованию.

Какую из этих двух форм записи выбрать зависит от вашего стиля программирования и конкретной задачи. Если вы определяете объект с помощью инициализатора {},  то вам, возможно, будет удобнее воспользоваться первой формой. Такая запись будет более естсетвенной и компактной. Однако, если геттеры/сеттеры требуется добавить позднее — потому что вы не определяете прототип или отдельный объект — то вторая форма будет единственно возможной. Она, возможно, лучшая демонстрация динамической природы JavaScript — но, в то же время, она может сделать код более тяжело воспринимаемым человеком.

До Firefox 3.0, геттеры и сеттеры не поддерживались для элементов DOM. Старые версии Firefox просто игнорируют это выражение. Если требуется исключение в этих случаях, изменение прототипа HTMLElement (HTMLElement.prototype.__define[SG]etter__) и выброс исключения как костыли для этого решения.

С Firefox 3.0, попытка определить геттер или сеттер на уже существующее свойство выбросит исключение.  Свойство должно быть удалено предварительно, что не является осуществимым для старых версий Firefox.

Смотрите также

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

Вы можете удалить свойство используя оператор delete. След. код прказывает как удалить свойство.

//Creates a new object, myobj, with two properties, a and b.
var myobj = new Object;
myobj.a = 5;
myobj.b = 12;

//Removes the a property, leaving myobj with only the b property.
delete myobj.a;

Вы также можете воспользоваться delete чтобы удалить глобальную переменную если ключевое слово var не было использовано чтобы объявить переменную:

g = 17;
delete g;

Смотри delete чтобы получить дополнительную информацию.

Смотрите также

Метки документа и участники

Обновлялась последний раз: vzhikness,
Скрыть боковую панель