Объектно-ориентированное программирование. Объекты

Последнее обновление: 08.04.2018

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

Объекты

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

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

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

Есть несколько способов создания нового объекта.

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

Var user = new Object();

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

Выражение new Object() представляет вызов конструктора - функции, создающей новый объект. Для вызова конструктора применяется оператор new . Вызов конструктора фактически напоминает вызов обычной функции.

Второй способ создания объекта представляет использование фигурных скобок:

Var user = {};

На сегодняшний день более распространенным является второй способ.

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

После создания объекта мы можем определить в нем свойства. Чтобы определить свойство, надо после названия объекта через точку указать имя свойства и присвоить ему значение:

Var user = {}; user.name = "Tom"; user.age = 26;

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

Console.log(user.name); console.log(user.age);

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

Var user = { name: "Tom", age: 26 };

В этом случае для присвоения значения свойству используется символ двоеточия, а после определения свойства ставится запятая (а не точка с запятой).

Кроме того, доступен сокращенный способ определения свойств:

Var name = "Tom"; var age = 34; var user = {name, age}; console.log(user.name); // Tom console.log(user.age); // 34

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

Var name = "Tom"; var age = 34; var user = {name, age}; var teacher = {user, course: "JavaScript"}; console.log(teacher.user); // {name: "Tom", age: 34} console.log(teacher.course); // JavaScript

Методы объекта

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

Var user = {}; user.name = "Tom"; user.age = 26; user.display = function(){ console.log(user.name); console.log(user.age); }; // вызов метода user.display();

Как и в случае с функциями методы сначала определяются, а потом уже вызываются.

Также методы могут определяться непосредственно при определении объекта:

Var user = { name: "Tom", age: 26, display: function(){ console.log(this.name); console.log(this.age); } };

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

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

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

Var user = { name: "Tom", age: 26, display(){ console.log(this.name, this.age); }, move(place){ console.log(this.name, "goes to", place); } }; user.display(); // Tom 26 user.move("the shop"); // Tom goes to the shop

Синтаксис массивов

Существует также альтернативный способ определения свойств и методов с помощью синтаксиса массивов:

Var user = {}; user["name"] = "Tom"; user["age"] = 26; user["display"] = function(){ console.log(user.name); console.log(user.age); }; // вызов метода user["display"]();

Название каждого свойства или метода заключается в кавычки и в квадратные скобки, затем им также присваивается значение. Например, user["age"] = 26 .

При обращении к этим свойствам и методам можно использовать либо нотацию точки (user.name), либо обращаться так: user["name"]

Строки в качестве свойств и методов

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

Var user = { "name": "Tom", "age": 26, "display": function(){ console.log(user.name); console.log(user.age); } }; // вызов метода user.display();

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

Var user = { name: "Tom", age: 26, "full name": "Tom Johns", "display info": function(){ console.log(user.name); console.log(user.age); } }; console.log(user["full name"]); user["display info"]();

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

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

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

Delete объект.свойство

Либо использовать синтаксис массивов:

Delete объект["свойство"]

Например, удалим свойство:

Var user = {}; user.name = "Tom"; user.age = 26; user.display = function(){ console.log(user.name); console.log(user.age); }; console.log(user.name); // Tom delete user.name; // удаляем свойство // альтернативный вариант // delete user["name"]; console.log(user.name); // undefined

После удаления свойство будет не определено, поэтому при попытке обращения к нему, программа вернет значение undefined.

Объект – это неупорядоченная коллекция свойств. Свойство – это часть объекта имитирующая переменную. Свойство состоит из имени и значения.

В JavaScript есть три категории объектов:

  • Объекты базового типа – это объекты, определённые в спецификации ECMAScript. Например, объекты типа Array , Function , Date или RegExp являются объектами базового типа.
  • Объекты среды выполнения – это объекты, определённые в среде выполнения (такой как браузер). Например, объекты типа HTMLElement , являются объектами среды выполнения.
  • Пользовательские объекты – это любой объект, созданный в результате выполнения программного кода JavaScript.
Создание объекта

Объект можно создать с помощью литерала объекта или оператора new с конструктором.

Литерал объекта – это заключённый в фигурные скобки список из нуля или более свойств (пар имя: значение), разделённых запятыми. Именем свойства может быть любой допустимый идентификатор, строковой литерал (допускается использовать пустую строку) или число. Числовые имена свойств автоматически преобразуются в строки. Значением свойства может быть значение любого типа или выражение (значением свойства в этом случае станет результат вычисления выражения):

// Создание пустого объекта var o = {}; // Создание объекта с тремя свойствами var user = { name: "Homer", "age": 45, 1: true };

Создание объекта с помощью оператора new :

Var o = new Object();

Операции с объектом

Основные операции производимые с объектами – это добавление новых свойств, изменение уже существующих свойств, удаление свойств и обращение к свойствам.

Добавить новое свойство в объект можно присвоив свойству значение. Чтобы присвоить свойству значение, к нему нужно получить доступ. Для доступа к свойству используется один из операторов доступа: . (точка) или (квадратные скобки):

Обращение к свойству и изменение значения осуществляется точно так же (с помощью операторов доступа):

Var o = {x:5}; alert(o.x); // Обращение к свойству alert(o["x"]); // Обращение к свойству o.x = 10; // Изменяем значение

Удаление свойства осуществляется с помощью оператора delete :

Var o = {x:5}; alert("x" in o); // true delete o.x; alert("x" in o); // false

Для перебора свойств объекта используется цикл for-in :

Var obj = {x: 5, y: 10, str: "Hi!"}; for (var prop in obj) { alert(prop); }

Методы объекта

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

Var o = { sayHi: function() { alert("Hello!"); } }; o.sayHi(); // "Hello!"

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

Var o = { name: "Homer", sayName: function() { alert(this.name); } }; o.sayName(); // "Homer"

Вместо ключевого слова this можно использовать непосредственно имя объекта, но это не очень удобно, так как, если изменится имя объекта, в методах придётся также изменять имя:

Var o = { name: "Homer", sayName: function() { alert(o.name); } }; o.sayName(); // "Homer"

Условие задачи :

1. Есть три объекта (три автомобиля) : first_Car , second_Car и third_Car .

2. Каждый из объектов (автомобилей) имеет набор свойств и соответствующих им значений (характеристики автомобиля ).

3. Рассмотрим один из объектов:

var first_Car = {
make: "VAZ" , /* производитель */
model: 2106 , /* модель */
year: 1980 , /* год выпуска */
color: "beige" , /* цвет */
passengers: 5 , /* число пассажиров */
convertible: false, /* откидной верх */
mileage: 80000 /* пробег */
}

Свойства make и color имеют строковые значения;

Свойства model , year , passengers и mileage - числовые значения;

Свойство convertible принимает булево значение.

Нужно сделать следующее :

Написать функцию, которая проверяет автомобиль по двум параметрам (год выпуска и пробег) и возвращает булево значение true или false .

Подробности :

1. Функция имеет один параметр car , в качестве которого получает один из 3-х объектов. Например, выше рассмотренный автомобиль first_Car .

2. Функция должна работать с любым подобным объектом.

Функция для проверки объекта - true или false

/* 1-ый объект */
var first_Car = {
make: "VAZ",
model: 2106,
year: 1980 ,
color: "beige",
passengers: 5,
convertible: false,
mileage: 80000
}

/* 2-ой объект */
var second_Car = {
make: "VW",
model: "Passat b3",
year: 1990 ,
color: "neptune",
passengers: 5,
convertible: false,
mileage: 160000
}

/* 3-ий объект */
var third_Car = {
make: "Hyundai",
model: "Solaris",
year: 2012 ,
color: "wet asphalt",
passengers: 5,
convertible: false,
mileage: 15000
}


function good_Car(car) {
if (car. year < 2000 ){
return false;
}
else if (car. mileage > 50000 ){
return false;
}
else{
return true;
}
}

/* Вызов функции и Вывод результата */
var result = good_Car ( third_Car );

document . write ( result );

Комментарии к решению:

  • Итак, мы имеем три объекта (три автомобиля) , каждый из которых можно проанализировать при помощи функции good_Car .
  • Функция good_Car имеет один параметр car , в качестве которого может выступать любой из объектов (автомобилей) : first_Car , second_Car или third_Car : function good_Car(car) .
  • В теле Функции good_Car составлено условие, согласно которому:

    Если значение свойства year объекта car меньше 2000 (другими словами : если год выпуска автомобиля меньше 2 000) , то функция возвращает false;

    Иначе, если значение свойства mileage объекта car больше 50000 (если пробег автомобиля больше 50 000) , то функция возвращает false;

    Иначе функция возвращает true .

  • Далее мы вызываем функцию и в качестве параметра указываем объект third_Car (третий автомобиль) , который успешно проходит проверку. Результат работы функции заносится в переменную result :
    var result = good_Car(third_Car); .
  • Переменная result выводится на экран;
  • Два других объекта (автомобиля) не будут соответствовать требованиям условия.
Оптимизация кода

Продолжим работать с объектами в javascript .

Итак, рассмотренная выше функция при проверке объектов (автомобилей) выдает в результате true или false (истину или ложь) .

Можно немного улучшить качество восприятия решения рассмотренной задачи, то есть вместо true или false выводить какой-либо текст. Для этого составим условие для анализа результата.

/* 1-ый объект */
var first_Car = {
make: "VAZ",
model: 2106,
year: 1980 ,
color: "beige",
passengers: 5,
convertible: false,
mileage: 80000
}

/* 2-ой объект */
var second_Car = {
make: "VW",
model: "Passat b3",
year: 1990 ,
color: "neptune",
passengers: 5,
convertible: false,
mileage: 160000
}

/* 3-ий объект */
var third_Car = {
make: "Hyundai",
model: "Solaris",
year: 2012 ,
color: "wet asphalt",
passengers: 5,
convertible: false,
mileage: 15000
}

/* Функция для проверки объекта */
function good_Car(car) {
if (car. year < 2000 ){
return false;
}
else if (car. mileage > 50000 ){
return false;
}
else{
return true;
}
}


var result = good_Car ( third_Car );

if(result) {
document . write ("У Вас неплохой автомобиль: " + third_Car . year + " год выпуска, с пробегом " + third_Car .mileage + " км." );
}
else{
document . write ("Не будем говорить о Вашем автомобиле...." );
}

Решение с условием для результата - Результат...

Условие для анализа результата составлено следующим образом.

  • Выражение if(result) является сокращенной формой записи выражения
    if(result == true) .
  • Если результат работы функции good_Car является истинным true , то мы выводим на экран фразу: «У Вас неплохой автомобиль: 2012 год выпуска, с пробегом 15000 км.», где

    2012 и 15000 - это значения свойств year и mileage объекта third_Car .

  • Если же условие для проверки результата выдаст ложное значение false , то мы увидим: «Не будем говорить о Вашем автомобиле....». То есть рассматриваемый объект (автомобиль) не прошел проверку.
Оптимизация кода - Идем дальше - Добавляем функцию

Но и это еще не все. Посмотрите внимательно на фрагмент кода для вызова функции и анализа результата:

/* Вызов функции и Анализ результата */
var result = good_Car( third_Car );

if(result) {
document.write("У Вас неплохой автомобиль: " + third_Car .year + " год выпуска, с пробегом " + third_Car .mileage + " км.");
}
else{
document.write("Не будем говорить о Вашем автомобиле....");
}

Здесь объект third_Car (третий автомобиль) указывается трижды:

  • Первый раз при вызове функции good_Car он указан в качестве ее параметра: good_Car(third_Car) .
  • И далее он фигурирует еще дважды, когда мы к нему обращаемся для указания его свойств: third_Car.year и third_Car.mileage .

Мне это не понравилось, так как при анализе другого объекта (автомобиля) нам придется его имя также указывать трижды !!!

Чтобы добиться одноразового указания анализируемого объекта, нужно и результат работы функции good_Car и анализ этого результата (то есть весь ) занести в еще одну функцию.

/* 1-ый объект */
var first_Car = {
make: "VAZ",
model: 2106,
year: 1980 ,
color: "beige",
passengers: 5,
convertible: false,
mileage: 80000
}

/* 2-ой объект */
var second_Car = {
make: "VW",
model: "Passat b3",
year: 1990 ,
color: "neptune",
passengers: 5,
convertible: false,
mileage: 160000
}

/* 3-ий объект */
var third_Car = {
make: "Hyundai",
model: "Solaris",
year: 2012 ,
color: "wet asphalt",
passengers: 5,
convertible: false,
mileage: 15000
}

/* Функция для проверки объекта */
function good_Car(car ) {
if (car .year < 2000){
return false;
}
else if (car .mileage > 50000){
return false;
}
else{
return true;
}
}

/* Заносим результат работы функции good_Car и Анализ результата в еще одну функцию */
function itog(car ){
var result = good_Car(car );

If(result ) {
document.write("У Вас неплохой автомобиль: " + car .year + " год выпуска, с пробегом " + car .mileage + " км.");
}
else{
document.write("Не будем говорить о Вашем автомобиле....");
}
}

itog( third_Car );

Решение с использованием еще одной функции - Результат...

У Вас неплохой автомобиль: 2012 год выпуска, с пробегом 15000 км.

Приветствую всех, кто читает данную публикацию. Сегодня я хочу разобрать с вами ключевой инструмент языка – объекты JavaScript. Напомню, что js является кроссбраузерным и функционирует во всех ОС (windows, mac os и т.д.). В отличие от объектно-ориентированных языков программирования, в js реализация объектов значительно отличается от привычного функционала и вариаций использования экземпляров, например, в C#.

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

Что из себя представляет объект в JavaScript и какими возможностями обладает?

В js объектами являются простые ассоциативные массивы (их еще также называют хэшами).

Что же такое ассоциативный массив?

Это структура данных, в которой хранится какое-то количество информации, относящееся и описывающее определенный элемент. Все данные структурированы и связаны между собой как «ключ =>значение».

К примеру, вам нужно описать автомобили. Тогда вы создаете объект avto и описываете в массиве его характеристики. Я решил описать марку машины (name), ее цвет (color) и стоимость (price). Ниже я прикрепил код реализации описанного задания.

1 2 3 4 5 var avto = { name: "BMW 116i", color: "black", price: 588000 };

var avto = { name: "BMW 116i", color: "black", price: 588000 };

Вот вы видите один из способов создания объекта с именем «avto». Name, color и price являются ключами, по которым в ходе написания приложения можно будет обращаться.

Этим примером я забежал вперед, поэтому сейчас разберем все по порядку.

Создать объект можно несколькими способами:

var avto = {}; или var avto = new Object ();

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

Все про свойства

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

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

Таким образом, можно сразу создать все ключи или же объявлять их по мере поступления. И даже если в ходе написания программы вы обратитесь к несуществующим ключам, ошибки не будет. В этом случае вернется “undefined”.

Первый способ.

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

avto.name = “ BMW 116i”

А вот в такой способ к существующим ключам вы добавите еще один элемент:

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

Второй способ.

Ничем не отличается от первого, если сравнивать их предназначения. Однако этот способ обладает небольшим преимуществом. Для такого варианта используются квадратные скобки:

avto[“name”] = “ BMW 116i”

А приятным добавлением является возможность создавать наименование свойств в виде любой строки. К примеру,

avto[“name of the car”] = “ BMW 116i”

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

var avto = {}; avto.name = "BMW_116i"; avto.price = 588000; var key = "price"; // была запрошена цена машины alert(avto);

Теперь перейдем к удалению свойств. Здесь все очень просто. Для удаления используется команда delete . Так, если к последнему примеру снизу дописать вот такие 2 строки:

delete avto.price;

alert (avto);

То с вызовом alert во второй раз диалоговое окно выдаст “undefined”.

Несколько слов о компактности

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

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

Давайте-ка переберем наши свойства

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

Если вы знакомы с другими языками программирования, то знаете, что чаще всего циклы создаются при помощи слова for , далее в круглых скобках прописывается условие перебора элементов.

В js оно напоминает своим внешним видом цикл foreach из языка C#. Ознакомьтесь с общим видом конструкции:

for (var obj in object) { // выполнение перебора}

где obj отвечает за название перечисляемых ключей,

object – за их значения.

А теперь вот вам конкретный примерчик.

1 2 3 4 5 6 7 8 var avto = { name: "BMW 116i", color: "black", price: 588000 }; for (var obj in object) { alert(obj + ":" + object) }

var avto = { name: "BMW 116i", color: "black", price: 588000 }; for (var obj in object) { alert(obj + ":" + object) }

Настало время познакомиться с методами

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

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

Так, для создания метода, нужно объявить объект, а после начать писать команду, точь-в-точь напоминающую создание свойств. Однако после «=» пишется уже не значение, а ключевое слово function (переменная). А далее в фигурных скобках ведется перечисление действий.

Вот реализация данного механизма:

var avto ={} avto.name = “BMV” avto.year = 1999 avto.drive = function(k) { alert(«Автомобиль проехал»+n+« км. »)} avto.drive(300) avto.drive(450)

Как видите, этот пример содержит свойства и методы, вызов которых изначально идентичен.

В JS есть еще и конструкторы?

Так точно! В этом языке все, что использует ключевое слово «new », автоматически становится конструктором. Так, выше вы видели объявление пустого объекта в виде: avto = new Object ();. Это и есть конструктор.

Для наглядности рассмотрите представленные строки ниже.

var bob = new Object ();

bob.name = «Bob Smith»;

Однако это не весь арсенал возможностей. В js можно создавать свои собственные конструкторы и после использовать их для объявления новых объектов.

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

function Avto (name, price) {

this.name = name;

this.price = price;

Теперь при создании неограниченного количества объектов и применения к ним этого конструктора, все они будут относиться к одному классу. Например:

var car1 = new Avto («BMW», 650000);

var car2 = new Avto («Audi», 520000);

В добавок к этому внутри конструктора можно создавать методы.

Особенности наследования в JavaScript

Обычно во многих языках наследование основывается на классах, которые могут наследовать друг друга. Тогда можно услышать такие выражения, как «класс-предок», «дочерний класс» и т.д.

Однако в js все иначе. Здесь наследуются объекты.

Все наследование основывается на внутренней ссылке между объектами, которая известна под именем «прототип». Если к методу приписать через точку «.prototype», а далее прописать имя прототипа, то все объекты выбранного метода будут наследоваться от этого прототипа.

Перейдем к примеру.

function Transport (name) { this.name = name this.canDrive = true } var transport = new Transport ("avto") // создали объект transport function Bike (name) { this.name = name } Bike.prototype = transport // указываем, что все новые объекты этого класса будут использовать в качестве прототипа transport bike1 = new Bike ("for_sport") bike2= new Bike ("for_child") console.log(bike1.name) console.log(bike2.name) console.log(bike1.canDrive)

На этом я, пожалуй, закончу. Я рассказал вам о фундаментальных аспектах скриптового языка. Однако это только поверхностные знания. Далее будем углубляться. А пока не забывайте вступать в ряды моих подписчиков и делиться ссылкой на статью с друзьями. Удачи!

Пока-пока!

С уважением, Роман Чуешов

Прочитано: 97 раз

Объекты

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

Объект является неупорядоченной коллекцией свойств, каждое из которых имеет имя и значение. Имена свойств являются строками, поэтому можно сказать, что объекты отображают строки в значения. Такое отображение строк в значения может называться по-разному: возможно, вы уже знакомы с такой фундаментальной структурой данных, как «хеш», «словарь» или «ассоциативный массив». Однако объект представляет собой нечто большее, чем простое отображение строк в значения.

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

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

Любое значение в языке JavaScript, не являющееся строкой, числом, true, false, null или undefined, является объектом. И даже строки, числа и логические значения, не являющиеся объектами, могут вести себя как неизменяемые объекты (имеют объекты-обертки String, Number и т.п.).

Объекты являются изменяемыми значениями и операции с ними выполняются по ссылке, а не по значению. Если переменная x ссылается на объект, и выполняется инструкция var y = x; , в переменную y будет записана ссылка на тот же самый объект, а не его копия. Любые изменения, выполняемые в объекте с помощью переменной y , будут также отражаться на переменной x .

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

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

    Атрибут writable определяет доступность значения свойства для записи.

    Атрибут enumerable определяет доступность имени свойства для перечисления в цикле for/in.

    Атрибут configurable определяет возможность настройки, т.е. удаления свойства и изменения его атрибутов.

До появления стандарта ECMAScript 5 все свойства в объектах, создаваемые программой, доступны для записи, перечисления и настройки. В ECMAScript 5 предусматривается возможность настройки атрибутов ваших свойств.

В дополнение к свойствам каждый объект имеет три атрибута объекта :

    Атрибут class содержит строку с именем класса объекта и определяет тип объекта.

    Флаг extensible (в ECMAScript 5) указывает на возможность добавления новых свойств в объект.

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

Объект базового языка

Это объект или класс объектов, определяемый спецификацией ECMAScript. Массивы, функции, даты и регулярные выражения (например) являются объектами базового языка.

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

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

Пользовательский объект

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

Собственное свойство

Это свойство, определяемое непосредственно в данном объекте.

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

Это свойство, определяемое прототипом объекта.

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

Объекты можно создавать с помощью литералов объектов, ключевого слова new и (в ECMAScript 5) функции Object.create() .

Литералы объектов

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

Ниже приводится несколько примеров создания объектов:

Var empty = {}; // Объект без свойств var point = { x:0, y:0 }; // Два свойства var point2 = { x:point.x, y:point.y+1 }; // Более сложные значения var site = { "url site": "www..NET Framework", // и дефисами, поэтому исп. кавычки author: { // Значением этого свойства является firstname: "Alexandr", // объект. Обратите внимание, что surname: "Frolov" // имена этих свойств без кавычек. } };

В ECMAScript 5 (и в некоторых реализациях ECMAScript 3) допускается использовать зарезервированные слова в качестве имен свойств без кавычек. Однако в целом имена свойств, совпадающие с зарезервированными словами, в ECMA-Script 3 должны заключаться в кавычки. В ECMAScript 5 последняя запятая, следующая за последним свойством в литерале объекта, игнорируется. В большинстве реализаций ECMAScript 3 завершающие запятые также игнорируются, но IE интерпретирует их наличие как ошибку.

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

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

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

Var o = new Object(); // Создать новый пустой объект: то же, что и {} var a = new Array(); // Создать пустой массив: то же, что и var d = new Date(); // Создать объект Date, представляющий текущее время var r = new RegExp("js"); // Создать объект RegExp для операций сопоставления с шаблоном

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

Object.create()

Стандарт ECMAScript 5 определяет метод Object.create(), который создает новый объект и использует свой первый аргумент в качестве прототипа этого объекта. Дополнительно Object.create() может принимать второй необязательный аргумент, описывающий свойства нового объекта.

Object.create() является статической функцией, а не методом, вызываемым относительно некоторого конкретного объекта. Чтобы вызвать эту функцию, достаточно передать ей желаемый объект-прототип:

// obj наследует свойства x и y var obj = Object.create({x:1, y:2});

Чтобы создать объект, не имеющий прототипа, можно передать значение null, но в этом случае вновь созданный объект не унаследует ни каких-либо свойств, ни базовых методов, таких как toString() (а это означает, что этот объект нельзя будет использовать в выражениях с оператором +):

// obj2 не наследует ни свойств, ни методов var obj2 = Object.create(null);

Если в программе потребуется создать обычный пустой объект (который, например, возвращается литералом {} или выражением new Object()), передайте в первом аргументе Object.prototype:

// obj3 подобен объекту, созданному // с помощью {} или new Object() var obj3 = Object.create(Object.prototype);

Возможность создавать новые объекты с произвольными прототипами (скажем иначе: возможность создавать «наследников» от любых объектов) является мощным инструментом, действие которого можно имитировать в ECMAScript 3 с помощью функции, представленной в примере ниже:

// inherit() возвращает вновь созданный объект, наследующий свойства // объекта-прототипа p. Использует функцию Object.create() из ECMAScript 5, // если она определена, иначе используется более старый прием. function inherit(p) { if (p == null) throw TypeError(); // p не может быть значением null if (Object.create) // Если Object.create() определена... return Object.create(p); // использовать ее. var t = typeof p; // Иначе выяснить тип и проверить его if (t !== "object" && t !== "function") throw TypeError(); function f() {}; // Определить пустой конструктор. f.prototype = p; // Записать в его свойство prototype // ссылку на объект p. return new f(); // Использовать f() для создания // "наследника" объекта p. }

Реализация функции inherit() приобретет больше смысла, как только мы познакомимся с конструкторами в следующей статье. А пока просто считайте, что она возвращает новый объект, наследующий свойства объекта в аргументе. Обратите внимание, что функция inherit() не является полноценной заменой для Object.create(): она не позволяет создавать объекты без прототипа и не принимает второй необязательный аргумент, как Object.create().

Получение и изменение свойств

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

// Простой объект var user = { login:"kot86", name:"Alexandr", age:26 }; var login = user.login; // Получить свойство "login" объекта user var name = user.name; // Получить свойство "name" объекта user var age = user["age"]; // Получить свойство "age" объекта user

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

User.age = 28; // Изменить значение свойства "age" user["login"] = "kot84"; // Изменить значение свойства "login" user["surname"] = "Frolov"; // Создать новое свойство "surname"

В ECMAScript 3 идентификатор, следующий за точкой, не может быть зарезервированным словом: нельзя записать обращение к свойству o.for или o.class, потому что for является ключевым словом, а class - словом, зарезервированным для использования в будущем.

Если объект имеет свойства, имена которых совпадают с зарезервированными словами, для доступа к ним необходимо использовать форму записи с квадратными скобками: o["for"] и o["class"]. Стандарт ECMAScript 5 ослабляет это требование (как это уже сделано в некоторых реализациях ECMAScript 3) и допускает возможность использования зарезервированных слов после оператора точки.

Прототипы

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

Все объекты, созданные с помощью литералов объектов, имеют один и тот же объект-прототип, на который в программе JavaScript можно сослаться так: Object.prototype .

Объекты, созданные с помощью ключевого слова new и вызова конструктора, в качестве прототипа получают значение свойства prototype функции-конструктора. Поэтому объект, созданный выражением new Object(), наследует свойства объекта Object.prototype, как если бы он был создан с помощью литерала в фигурных скобках {}. Аналогично прототипом объекта, созданного выражением new Array(), является Array.prototype, а прототипом объекта, созданного выражением new Date(), является Date.prototype.

Object.prototype - один из немногих объектов, которые не имеют прототипа: у него нет унаследованных свойств. Другие объекты-прототипы являются самыми обычными объектами, имеющими собственные прототипы.

Все встроенные конструкторы (и большинство пользовательских конструкторов) наследуют прототип Object.prototype. Например, Date.prototype наследует свойства от Object.prototype, поэтому объект Date, созданный выражением new Date(), наследует свойства от обоих прототипов, Date.prototype и Object.prototype. Такая связанная последовательность объектов-прототипов называется цепочкой прототипов.

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

Объекты в языке JavaScript обладают множеством «собственных свойств» и могут также наследовать множество свойств от объекта-прототипа. Чтобы разобраться в этом, необходимо внимательно изучить механизм доступа к свойствам. В примерах этого раздела для создания объектов с определенными прототипами используется функция inherit(), показанная выше.

Предположим, что программа обращается к свойству x объекта obj. Если объект obj не имеет собственного свойства с таким именем, выполняется попытка отыскать свойство x в прототипе объекта obj. Если объект-прототип не имеет собственного свойства с этим именем, но имеет свой прототип, выполняется попытка отыскать свойство в прототипе прототипа. Так продолжается до тех пор, пока не будет найдено свойство x или пока не будет достигнут объект, не имеющий прототипа. Как видите, атрибут prototype объекта создает цепочку, или связанный список объектов, от которых наследуются свойства.

Var obj = {}; // obj наследует методы объекта Object.prototype obj.x = 1; // и обладает собственным свойством x. var p = inherit(obj); // p наследует свойства объектов obj и Object.prototype p.y = 2; // и обладает собственным свойством y. var q = inherit(p); // q наследует свойства объектов p, obj и Object.prototype q.z = 3; // и обладает собственным свойством z. var s = q.toString(); // toString наследуется от Object.prototype var d = q.x + q.y // Результат 3: x и y наследуются от obj и p

Теперь предположим, что программа присваивает некоторое значение свойству x объекта obj. Если объект obj уже имеет собственное свойство (не унаследованное) с именем x, то операция присваивания просто изменит значение существующего свойства. В противном случае в объекте obj будет создано новое свойство с именем x. Если прежде объект obj наследовал свойство x, унаследованное свойство теперь окажется скрыто вновь созданным собственным свойством с тем же именем.

Операция присваивания значения свойству проверит наличие этого свойства в цепочке прототипов, чтобы убедиться в допустимости присваивания. Например, если объект obj наследует свойство x, доступное только для чтения, то присваивание выполняться не будет. Однако если присваивание допустимо, всегда создается или изменяется свойство в оригинальном объекте и никогда в цепочке прототипов. Тот факт, что механизм наследования действует при чтении свойств, но не действует при записи новых значений , является ключевой особенностью языка JavaScript, потому что она позволяет выборочно переопределять унаследованные свойства:

Var unitcircle = { r:1 }; // Объект, от которого наследуется свойство var c = inherit(unitcircle); // c наследует свойство r c.x = 1; c.y = 1; // c определяет два собственных свойства c.r = 2; // c переопределяет унаследованное свойство console.log(unitcircle.r); // => 1: объект-прототип не изменился

Существует одно исключение из этого правила, когда операция присваивания значения свойству терпит неудачу или приводит к созданию/изменению свойства оригинального объекта. Если объект obj наследует свойство x и доступ к этому свойству осуществляется посредством методов доступа, то вместо создания нового свойства x в объекте obj производится вызов метода записи нового значения. Однако обратите внимание, что метод записи вызывается относительно объекта obj, а не относительно прототипа, в котором определено это свойство, поэтому, если метод записи определяет какие-либо свойства, они будут созданы в объекте obj, а цепочка прототипов опять останется неизменной.

Ошибки доступа к свойствам

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

Попытка обращения к несуществующему свойству не считается ошибкой. Если свойство x не будет найдено среди собственных или унаследованных свойств объекта obj, выражение обращения к свойству obj.x вернет значение undefined.

Однако попытка обратиться к свойству несуществующего объекта считается ошибкой. Значения null и undefined не имеют свойств, и попытки обратиться к свойствам этих значений считаются ошибкой:

// Простой объект var user = { login:"kot86", name:"Alexandr", age:26 }; var a = user.password; // undefined: свойство отсутствует // Возбудит исключение TypeError. // Значение undefined не имеет свойства length var len = user.password.length;

Если нет уверенности, что user и user.password являются объектами (или ведут себя подобно объектам), нельзя использовать выражение user.password.length, так как оно может возбудить исключение. Ниже демонстрируются два способа защиты против исключений подобного рода:

// Более наглядный и прямолинейный способ var len = undefined; if (user) { if (user.password) len = user.password.length; } // Более краткая и характерная для JavaScript альтернатива // получения длины значения свойства password var len = user && user.password && user.password.length;

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

// Свойства prototype встроенных конструкторов доступны только для чтения Object.prototype = 0; // Присваивание не возбудит исключения; // значение Object.prototype не изменится

Этот исторически сложившийся недостаток JavaScript исправлен в строгом режиме, определяемом стандартом ECMAScript 5. Все неудачные попытки изменить значение свойства в строгом режиме приводят к исключению TypeError.

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

    Объект obj имеет собственное свойство p, доступное только для чтения: нельзя изменить значение свойства, доступного только для чтения. (Обратите, однако, внимание на метод defineProperty(), который представляет собой исключение, позволяющее изменять значения настраиваемых свойств, доступных только для чтения.)

    Объект obj имеет унаследованное свойство p, доступное только для чтения: унаследованные свойства, доступные только для чтения, невозможно переопределить собственными свойствами с теми же именами.

    Объект obj не имеет собственного свойства p; объект obj не наследует свойство p с методами доступа и атрибут extensible объекта obj имеет значение false. Если свойство p отсутствует в объекте obj и для него не определен метод записи, то операция присваивания попытается добавить свойство p в объект obj. Но поскольку объект obj не допускает возможность расширения, то попытка добавить в него новое свойство потерпит неудачу.

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

Оператор delete удаляет свойство из объекта. Его единственный операнд должен быть выражением обращения к свойству. Может показаться удивительным, но оператор delete не оказывает влияния на значение свойства - он оперирует самим свойством:

// Простой объект var user = { login:"kot86", name:"Alexandr", age:26 }; delete user.login; // Теперь объект user не имеет свойства login delete user["name"]; // Теперь объект user не имеет свойства name

Оператор delete удаляет только собственные свойства и не удаляет унаследованные. (Чтобы удалить унаследованное свойство, необходимо удалять его в объекте-прототипе, в котором оно определено. Такая операция затронет все объекты, наследующие этот прототип.)

Выражение delete возвращает значение true в случае успешного удаления свойства или когда операция удаления не привела к изменению объекта (например, при попытке удалить несуществующее свойство). Выражение delete также возвращает true, когда этому оператору передается выражение, не являющееся выражением обращения к свойству:

Obj = {x:1}; // obj имеет собственное свойство x и наследует toString delete obj.x; // Удалит x и вернет true delete obj.x; // Ничего не сделает (x не существует) и вернет true delete obj.toString; // Ничего не сделает (toString не собственное свойство) и вернет true delete 1; // Бессмысленно, но вернет true

Оператор delete не удаляет ненастраиваемые свойства, атрибут configurable которых имеет значение false. (Однако он может удалять настраиваемые свойства нерасширяемых объектов.) Ненастраиваемыми являются свойства встроенных объектов, а также свойства глобального объекта, созданные с помощью инструкций объявления переменных и функций. Попытка удалить ненастраиваемое свойство в строгом режиме вызывает исключение TypeError. В нестрогом режиме (и в реализациях ECMAScript 3) в таких случаях оператор delete просто возвращает false:

Delete Object.prototype; // Удаление невозможно - ненастраиваемое свойство var x = 1; // Объявление глобальной переменной delete this.x; // Это свойство нельзя удалить function f() {} // Объявление глобальной функции delete this.f; // Это свойство также нельзя удалить

Проверка существования свойств

Объекты в языке JavaScript можно рассматривать как множества свойств, и нередко бывает полезно иметь возможность проверить принадлежность к множеству - проверить наличие в объекте свойства с данным именем. Выполнить такую проверку можно с помощью оператора in , с помощью методов hasOwnProperty() и propertyIsEnumerable() или просто обратившись к свойству.

Оператор in требует, чтобы в левом операнде ему было передано имя свойства (в виде строки) и объект в правом операнде. Он возвращает true, если объект имеет собственное или унаследованное свойство с этим именем:

Var obj = { x:1 } "x" in obj; // true: obj имеет собственное свойство "x" "y" in obj; // false: obj не имеет свойства "y" "toString" in obj; // true: obj наследует свойство toString

Метод hasOwnProperty() объекта проверяет, имеет ли объект собственное свойство с указанным именем. Для наследуемых свойств он возвращает false:

Var obj = { x:1 } obj.hasOwnProperty("x"); // true: obj имеет собственное свойство "x" obj.hasOwnProperty("y"); // false: obj не имеет свойства "y" obj.hasOwnProperty("toString"); // false: toString - наследуемое свойство

Метод propertyIsEnumerable() накладывает дополнительные ограничения по сравнению с hasOwnProperty(). Он возвращает true, только если указанное свойство является собственным свойством, атрибут enumerable которого имеет значение true. Свойства встроенных объектов не являются перечислимыми. Свойства, созданные обычной программой на языке JavaScript, являются перечислимыми, если не был использован один из методов ECMAScript 5, представленных ниже, которые делают свойства неперечислимыми.

Часто вместо оператора in достаточно использовать простое выражение обращения к свойству и использовать оператор!== для проверки на неравенство значению undefined:

Var obj = { x:1 } obj.x !== undefined; // true: obj имеет свойство "x" obj.y !== undefined; // false: obj не имеет свойства "y" obj.toString !== undefined; // true: obj наследует свойство toString

Однако оператор in отличает ситуации, которые неотличимы при использовании представленного выше приема на основе обращения к свойству. Оператор in отличает отсутствие свойства от свойства, имеющего значение undefined.

Перечисление свойств

Вместо проверки наличия отдельных свойств иногда бывает необходимо обойти все имеющиеся свойства или получить список всех свойств объекта. Обычно для этого используется цикл for/in, однако стандарт ECMAScript 5 предоставляет две удобные альтернативы.

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

// Простой объект с тремя перечислимыми свойствами var user = { login:"kot86", name:"Alexandr", age:26 }; user.propertyIsEnumerable("toString"); // false, toString - встроенный метод for (n in user) console.log(n);

Некоторые библиотеки добавляют новые методы (или другие свойства) в объект Object.prototype, чтобы они могли быть унаследованы и быть доступны всем объектам. Однако до появления стандарта ECMAScript 5 отсутствовала возможность сделать эти дополнительные методы неперечислимыми, поэтому они оказывались доступными для перечисления в циклах for/in. Чтобы решить эту проблему, может потребоваться фильтровать свойства, возвращаемые циклом for/in. Ниже приводятся два примера реализации такой фильтрации:

For (n in user) { if (!user.hasOwnProperty(n)) continue; console.log(n); } for (n in user) { if (typeof user[n] === "function") continue; console.log(n); }

В дополнение к циклу for/in стандарт ECMAScript 5 определяет две функции, перечисляющие имена свойств. Первая из них, Object.keys() , возвращает массив имен собственных перечислимых свойств объекта.

Вторая функция ECMAScript 5, выполняющая перечисление свойств - Object.getOwnPropertyNames() . Она действует подобно функции Object.keys(), но возвращает имена всех собственных свойств указанного объекта, а не только перечислимые. В реализациях ECMAScript 3 отсутствует возможность реализовать подобные функции, потому что ECMAScript 3 не предусматривает возможность получения неперечислимых свойств объекта.

Методы чтения и записи свойств

Выше уже говорилось, что свойство объекта имеет имя, значение и набор атрибутов. В ECMAScript 5 значение может замещаться одним или двумя методами, известными как методы чтения (getter) и записи (setter) . Свойства, для которых определяются методы чтения и записи, иногда называют свойствами с методами доступа , чтобы отличать их от свойств с данными, представляющих простое значение.

Когда программа пытается получить значение свойства с методами доступа, интерпретатор вызывает метод чтения (без аргументов). Возвращаемое этим методом значение становится значением выражения обращения к свойству. Когда программа пытается записать значение в свойство, интерпретатор вызывает метод записи, передавая ему значение, находящее справа от оператора присваивания. Этот метод отвечает за «установку» значения свойства. Значение, возвращаемое методом записи, игнорируется.

В отличие от свойств с данными, свойства с методами доступа не имеют атрибута writable. Если свойство имеет оба метода, чтения и записи, оно доступно для чтения/записи. Если свойство имеет только метод чтения, оно доступно только для чтения. А если свойство имеет только метод записи, оно доступно только для записи (такое невозможно для свойств с данными) и попытки прочитать значение такого свойства всегда будут возвращать undefined.

Самый простой способ определить свойство с методами доступа заключается в использовании расширенного синтаксиса определения литералов объектов:

Var obj = { // Обычное свойство с данными data_prop: value, // Свойство с методами доступа определяется как пара функций get accessor_prop() { /* тело функции */ }, set accessor_prop(value) { /* тело функции */ } };

Свойства с методами доступа определяются как одна или две функции, имена которых совпадают с именем свойства и с заменой ключевого слова function на get и/или set.

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

Для примера рассмотрим следующий объект, представляющий декартовы координаты точки на плоскости. Для представления координат X и Y в нем имеются обычные свойства с данными, а также свойства с методами доступа, позволяющие получить эквивалентные полярные координаты точки:

Var p = { // x и y обычные свойства с данными, доступные для чтения/записи x: 1.0, y: 1.0, // r - доступное для чтения/записи свойство с двумя методами доступа. // Не забывайте добавлять запятые после методов доступа get r() { return Math.sqrt(this.x*this.x + this.y*this.y); }, set r(newvalue) { var oldvalue = Math.sqrt(this.x*this.x + this.y*this.y); var ratio = newvalue/oldvalue; this.x *= ratio; this.y *= ratio; }, // theta - доступное только для чтения свойство с единственным методом чтения get theta() { return Math.atan2(this.y, this.x); } };

Обратите внимание на использование ключевого слова this в методах чтения и записи выше. Интерпретатор будет вызывать эти функции, как методы объекта, в котором они определены, т.е. в теле функции this будет ссылаться на объект точки. Благодаря этому метод чтения свойства r может ссылаться на свойства x и y, как this.x и this.y.

Свойства с методами доступа наследуются так же, как обычные свойства с данными, поэтому объект p, определенный выше, можно использовать как прототип для других объектов точек. В новых объектах можно определять собственные свойства x и y, и они будут наследовать свойства r и theta.

Атрибуты объекта

Все объекты имеют атрибуты prototype, class и extensible. Все эти атрибуты описываются в подразделах ниже.

Атрибут prototype

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

Атрибут prototype устанавливается в момент создания объекта. Для объектов, созданных с помощью литералов, прототипом является Object.prototype. Прототипом объекта, созданного с помощью оператора new, является значение свойства prototype конструктора. А прототипом объекта, созданного с помощью Object.create(), становится первый аргумент этой функции (который может иметь значение null).

Стандартом ECMAScript 5 предусматривается возможность определить прототип любого объекта, если передать его методу Object.getPrototypeOf() . В ECMAScript 3 отсутствует эквивалентная функция, но зачастую определить прототип объекта obj можно с помощью выражения obj.constructor.prototype.

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

Обратите внимание, что объекты, созданные с помощью литералов объектов или Object.create(), получают свойство constructor, ссылающееся на конструктор Object(). Таким образом, constructor.prototype ссылается на истинный прототип для литералов объектов, но обычно это не так для объектов, созданных вызовом Object.create().

Чтобы определить, является ли один объект прототипом (или звеном в цепочке прототипов) другого объекта, следует использовать метод isPrototypeOf() . Чтобы узнать, является ли p прототипом obj, нужно записать выражение p.isPrototypeOf(obj). Например:

Var p = {x:1}; // Определить объект-прототип. var obj = Object.create(p); // Создать объект с этим прототипом. p.isPrototypeOf(obj); // => true: obj наследует p Object.prototype.isPrototypeOf(p); // => true: p наследует Object.prototype

Атрибут class

Атрибут class объекта - это строка, содержащая информацию о типе объекта. Ни в ECMAScript 3, ни в ECMAScript 5 не предусматривается возможность изменения этого атрибута и предоставляются лишь косвенные способы определения его значения. По умолчанию метод toString() (наследуемый от Object.prototype) возвращает строку вида:

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

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

// Название класса объекта function classof(obj) { if (obj === null) return "Null"; if (obj === undefined) return "Undefined"; return Object.prototype.toString.call(obj).slice(8,-1); }

Этой функции classof() можно передать любое значение, допустимое в языке JavaScript. Числа, строки и логические значения действуют подобно объектам, когда относительно них вызывается метод toString(), а значения null и undefined обрабатываются особо.

Атрибут extensible

Атрибут extensible объекта определяет, допускается ли добавлять в объект новые свойства. В ECMAScript 3 все встроенные и определяемые пользователем объекты неявно допускали возможность расширения, а расширяемость объектов среды выполнения определялась каждой конкретной реализацией. В ECMAScript 5 все встроенные и определяемые пользователем объекты являются расширяемыми, если они не были преобразованы в нерасширяемые объекты, а расширяемость объектов среды выполнения по-прежнему определяется каждой конкретной реализацией.

Стандарт ECMAScript 5 определяет функции для получения и изменения признака расширяемости объекта. Чтобы определить, допускается ли расширять объект, его следует передать методу Object.isExtensible() . Чтобы сделать объект нерасширяемым, его нужно передать методу Object.preventExtensions() . Обратите внимание, что после того как объект будет сделан нерасширяемым, его нельзя снова сделать расширяемым. Отметьте также, что вызов preventExtensions() оказывает влияние только на расширяемость самого объекта. Если новые свойства добавить в прототип нерасширяемого объекта, нерасширяемый объект унаследует эти новые свойства.

Назначение атрибута extensible заключается в том, чтобы дать возможность «фиксировать» объекты в определенном состоянии, запретив внесение изменений. Атрибут объектов extensible часто используется совместно с атрибутами свойств configurable и writable, поэтому в ECMAScript 5 определяются функции, упрощающие одновременную установку этих атрибутов.

Метод Object.seal() действует подобно методу Object.preventExtensions(), но он не только делает объект нерасширяемым, но и делает все свойства этого объекта недоступными для настройки. То есть в объект нельзя будет добавить новые свойства, а существующие свойства нельзя будет удалить или настроить. Однако существующие свойства, доступные для записи, по-прежнему могут быть изменены.

После вызова Object.seal() объект нельзя будет вернуть в прежнее состояние. Чтобы определить, вызывался ли метод Object.seal() для объекта, можно вызвать метод Object.isSealed() .

Метод Object.freeze() обеспечивает еще более жесткую фиксацию объектов. Помимо того, что он делает объект нерасширяемым, а его свойства недоступными для настройки, он также делает все собственные свойства с данными доступными только для чтения. (Это не относится к свойствам объекта с методами доступа, обладающими методами записи; эти методы по-прежнему будут вызываться инструкциями присваивания.) Чтобы определить, вызывался ли метод Object.freeze() объекта, можно вызвать метод Object.isFrozen() .

Важно понимать, что Object.seal() и Object.freeze() воздействуют только на объект, который им передается: они не затрагивают прототип этого объекта. Если в программе потребуется полностью зафиксировать объект, вам, вероятно, потребуется зафиксировать также объекты в цепочке прототипов.

Сериализация объектов

Сериализация объектов - это процесс преобразования объектов в строковую форму представления, которая позднее может использоваться для их восстановления. Для сериализации и восстановления объектов JavaScript стандартом ECMAScript 5 предоставляются встроенные функции JSON.stringify() и JSON.parse() . Эти функции используют формат обмена данными JSON. Название JSON происходит от «JavaScript Object Notation» (форма записи объектов JavaScript), а синтаксис этой формы записи напоминает синтаксис литералов объектов и массивов в языке JavaScript:

Var obj = {x:1, y:{z:}}; // Определить испытательный объект var s = JSON.stringify(obj); // s == "{"x":1,"y":{"z":}}" var p = JSON.parse(s); // p - копия объекта obj

Синтаксис формата JSON является лишь подмножеством синтаксиса языка JavaScript и не может использоваться для представления всех возможных значений, допустимых в JavaScript. Поддерживаются и могут быть сериализованы и восстановлены: объекты, массивы, строки, конечные числовые значения, true, false и null. Значения NaN, Infinity и -Infinity сериализуются в значение null. Объекты Date сериализуются в строки с датами в формате ISO, но JSON.parse() оставляет их в строковом представлении и не восстанавливает первоначальные объекты Date.

Объекты Function, RegExp и Error и значение undefined не могут быть сериализованы или восстановлены. Функция JSON.stringify() сериализует только перечислимые собственные свойства объекта. Если значение свойства не может быть сериализовано, это свойство просто исключается из строкового представления. Обе функции, JSON.stringify() и JSON.parse(), принимают необязательный второй аргумент, который можно использовать для настройки процесса сериализации и/или восстановления, например, посредством определения списка свойств, подлежащих сериализации, или функции преобразования значений во время сериализации.