Объекты в JavaScript
Объект — комплексный тип данных, который формируется на основе класса, функции-конструктора или объявляется отдельно. Объекты обладают свойствами и методами, которые обрабатывают свойства объекта. Объект представляет собой ссылочный тип данных, свойства и методы которого хранятся в куче, а не стеке. Объект также может представлять собой ассоциативный массив.
Структура объектов и их объявление
Как известно, объекты в JavaScript являются экземплярами класса или функции-конструктора. Объекты получают все публичные методы и свойства класса? функции-конструктора или прототипа. Объявить объект можно следующим способом:
- var obj = {// свойства и методы} — объявление без конструктора, классом наследником выступает Object.prototype;
- var obj = new Object({}) — то же, что и предыдущий способ, только через new;
- var obj = new Func_constructor() — создание через функцию конструктор;
- var obj = new Class_name() — создание через класс.
Рассмотрим эти объявления на примерах.
Пример создание объектов разными способами:
// Объявление объекта посредством квадратных скобок без объявления класса или функции-конструктора
var car = {
color: «red»,
start: function(){alert(«car is started»)}
};//обратимся к свойству
alert(car.color); // red
car.start(); // car is started//Объявление объекта через конструктор new
var car = new Object({calor: ‘red’, start:function(){alert(«car is started»)}});// функция конструктор
function Car(color){
this.color = color;
this.start = function(){alert(‘car is started’);}
}
//создание объекта
var obj = new Car(«red»); // объект получил и свойство из параметра в конструкторе и метод — все объекты этого конструктора будут получать разные цвета но один и тот же метод// Создание через конструктор класса
сlass Car{
// свойства и методы
}
var obj = new Car();
Свойства объектов
Если сравнивать объекты программы с объектами реального мира, то получится такая картина: человек, автомобиль, здание, телевизор и другое обладают своими свойствами. Это может быть рост, вес, габариты, цвет материал. Классы описывают объекты: они говорят какими свойствами и методами будут обладать все объекты определенного класса. Разберем на примере создания объекта автомобиля.
Пример:
var Volvo = {
model: «Volvo», // свойство, описывающее модель
type: «Sedan», // свойство, описывающее тип кузова
width: 5, // свойство, описывающее длину
height: 2 // свойство, описывающее высоту
};// В нашем случае объект автомобиля Volvo обладает несколькими свойствами. В реальном мире у него гораздо больше свойств. Но в целях программы нам нужны только эти.
// Напишем алгоритм, который при клике на кнопку выдает информацию об автомобиле<button id = «Volvo» value = «Volvo»> // RКнопка для события
<p id = «car_info»></p> // Место отображения информацииfunction gerCarParameters(e){ // функция вывода информации на экран
if(e.target.value == Volvo.model) //если модель объекта соответствует модели, указанной на кнопке, то выводится информация
document.getElementById(«car_info»).innerHTML = Volvo.model + «<br>» + Volvo.type + «<br>» + Volvo.width + «<br>» + Volvo.height;
else throw «Car of this model isn’t in database» // Если объекта нет то выводится сообщение
}document.getElementById(«Volvo»).onclick = «getCarParameters(event)» // Присваиваем кнопке атрибут события и функцию-обработчик
Данный пример демонстрирует, как могут быть использованы объекты в программировании. Если уже совсем просто, то объекты хранят определенный тип информации. Мы инкапсулируем данные в объекте, которые относятся к текущему объекту.
Также доступ к свойствам может осуществляется по примеру ассоциативного массива.
Пример:
alert(Volvo[‘type’]); // Sedan
alert(Volvo[«width»]); // 5
Структурные свойства
Свойства могут быть объектами, массивами и всеми распространенными типами данных в JavaScript
Пример: Присвоим объекту разные типы данных
var car = {
model: «Volvo», // String
type: «Sedan», // String
width: 5, // Int
height: 2 //Int
onSale: true // Bool
popular_in_counries: [«England», «France», «USA»], // Array
engine: { // Object
type: «Caterpillar»,
power: 250,
celinder_number: 8
}
};alert(car.engine.type); // Caterpillar
alert(car.popular_in_contries); // England, France, USA
car.popular_in_contries.push(«Russia»);
alert(car.popular_in_contries); // Russia, England, France, USA
Особого внимания заслуживает свойство в виде объекта. Цепочку можно организовать по разносу: вложенных объектов может быть сколько угодно.
Добавление свойств в объект, созданный конструктором
Если есть конструктор, который добавляет определенные свойства объекту или вовсе создает его пустым, то свойства можно добавить динамически.
Пример:
function Car(){}
var obj = new Car(); // Создали пустой объектobj.color = «red»; // добавили свойства текущему объекту
obj[«year»] = 1990; // как ассоциативный массив, то же, что и предыдущий пример
alert(obj.year); // 1990
Методы объектов
Методы объектов представляют собой функцию, которая проводит некие манипуляции со свойствами объектов или некие действия, не касаясь свойств.
Пример:
var human = {
height: 180,
weight: 75,
go: function(){alert(«go»);}, // метод
get_parameters: function(){alert(this.height + » » + this.weight)}
}human.go() // go
human.get_parameters(); // 180 75
// или так
human[«go»](); // go
Как и свойства, методы могут присваиваться и динамически после создания объекта. В коде видно конструкцию «this» — о ней поговорим позже. Аналогично методы объявляются в классах и конструкторах.
Что представляет собой объект в рамках JavaScript и ООП в целом?
Объекты в ООП — набор данных и функций, доступ к которым осуществляется по ссылке. Ссылкой является переменная, в которую сохраняется созданный объект. Объект представляет собой ссылочный тип данных, когда примитивы (Integer, String и т.п) хранятся в стеке. Примитивам нельзя присвоить свойства и методы, если они не объявлены через оператор new.
Пример:
var x = 5; // примитив, значение которого хранится в стеке
var y = new Number(5); // Объект, данные которого хранятся в куче
x.a = 5; // ошибка присвоения свойства примитиву
y.a = 6; // добавлено свойство
typeof x; // Number
typeof y; // Object
Мы сохраняем объект в переменную и далее через нее можно получить доступ к его свойствам и методам. Однако что будет если создать объект без сохранения?
Пример:
new Car(); // Объект создается и тут же уничтожается интерпретатором
var x = new Car(); // сохраняем объект в переменную и теперь он нам доступен.
Переменная объекта это ссылка на блок свойств и методов в куче. В предыдущем примере такой ссылкой является переменная x. Рассмотрим простой пример.
Пример:
var x = {a: 5, b: 4}
var y = x // присвоили переменной объект xalert(x.a); // 5
alert(y.a); // 5
y.a = 6; изменили свойство объекта y
alert(y.a); // 6
alert(x.a); // 6 — кажется тут что то не так
Почему изменив объект y, изменился и объект х? На самом деле В переменные x и y сохранен один и тот же объект. Просто теперь у него две ссылки x и y. То есть: x и y = {a: 5, b: 4}, присвоить переменной y новый объект, его нужно объявить заново. А такое присвоение «y=x» попросту копирует ссылку на один и тот-же объект. Чтобы ставить одну ссылку, допустим x, то ссылке y нужно присвоить null: y = null. Таким образом мы не уничтожаем объект, но ссылку на него.
Строковое представление объектов
Что будет, если вывести имя ссылки на экран?
Пример:
var obj = {a:»Hello», b:»World»};
alert(obj); // [object Object]
Что это значит? Почему выводится такое значение. Ведь когда мы выводим массив, то выводятся все его значения, а объект таким образом.
Все объекты наследуют специальные методы и свойства, хранящиеся в Object — класс, находящийся на вершине иерархии объектов JavaScript, от которого наследуют функционал объекты, массивы, строки и остальные объекты. Object.prototype хранит в себе методы toString() b valueOf(), которые и отвечают за отображение значения [object Object] на экране.
У Array.prototype, String.prototype и других есть свои такие методы. Однако пользовательские объекты, не важно созданы они конструктором или простым присваиванием берут эти методы у Object.prototype. На экран выводится тип ссылки, указывающей, что это object, который наследуется от Object.
Если таким же способом вывести массив, число и другой встроенный объект, то отобразится его значение, а не тип. Такое отображение действует только на пользовательские объекты и объекты DOM.
Сравнение примитивов и объектов
Ранее упоминалось, чт примитивы это не объекты. Однако при создании примитивов посредством new, мы получаем объект. Сравните следующие отличия с помощью оператора typeof:
typeof «John» // «string»
typeof 3.14 // «number»
typeof true // «boolean»
typeof false // «boolean»
typeof x // «undefined» (if x has no value)
typeof {name:’John’, age:34} // «object»
typeof [1,2,3,4] // «object» не «array»
typeof null // «object» (null — тип данных)
typeof function myFunc(){} // «function»
Отдельно стоит рассмотреть функцию. Почему-то оператор typeof выводит function. Хотя это объект и наследуется он от Function.prototype. Не стоит вдаваться в подробности так как это особенность оператора. Следует знать, что функции — это объекты!
Отображение свойств объекта через цикл for in
Цикл for in прекрасно подходит для перебора объекта как массива.
Пример:
var obj = {a:»Hello», b:»World»};
for (x in obj) {
alert(obj[x] + » «); // Hello World
};
Добавление свойств и методов объекту через специальную функцию Object.defineProperty()
Данная функция является статической и находится в хранилище класса Object.prototype. Функция позволяет добавить в объект свойство и настроить его. В опции настроек входит: имя, значение, разрешение на перебор циклом for in
Пример:
var obj = {};
Object.defineOwnProperty(obj, «name», «john», true);
Пояснение:
- Первый параметр функции указывает какому объекту присваивается свойство.
- Второй параметр определяет имя свойства.
- Третий параметр определяет значение свойства.
- Четвертый параметр разрешает итерацию в цикле, если false, то нет.
Операции с объектами
Объекты могут участвовать в различных операциях. Ими можно манипулировать по разному, а также изменять динамически.
Пример: Объекты могут быть свойствами других объектов
var obj = {
n:9,
u: 10,
v: {a:5, b:7} // объект в объекте
};
Пример: Объект может быть параметром функции
function showObj(obj, x, y){
alert(obj.x + » «+ obj.y);
}var human = {weight: 80, height: 180}
showObj(human, weight, height); // 80 180
Пример: Методы объектов можно использовать для других объектов
var obj_1 = {a: 8, show: function(){alert(this.a)}};
var obj_2 = {a: 3};obj_1.show(); // 8
obj_2.show(); // ошибка — нет такой функции у данного объектаobj_1.show.call(obj_2); // 3
Последний вызов метода вывел результат для второго объекта. Функция call позволяет применять метод к контексту других объектов.
Пример: Новые статичные методы в ES 2015 для Object
// Добавляет или изменяет свойство объекта
Object.defineProperty(object, property, descriptor)// Добавляет или изменяет несколько свойств объекта
Object.defineProperties(object, descriptors)// Доступ к значению свойства
Object.getOwnPropertyDescriptor(object, property)// Возвращает все имена свойств массивом
Object.getOwnPropertyNames(object)// Возвращает свойства, доступные для цикла
Object.keys(object)// Показывает прототип объекта
Object.getPrototypeOf(object)// Запрещает добавлять свойства объекту
Object.preventExtensions(object)// Возвращает true, если разрешено добавление свойств в объект
Object.isExtensible(object)// Запрещает изменять имена свойств
Object.seal(object)// Возвращает true, если объект защищен от смены имен свойств
Object.isSealed(object)// Запрещает какие либо изменения в объекте
Object.freeze(object)// Возвращает true, если объект полностью защищен от изменений
Object.isFrozen(object)
Оператор this
Оператор this является ссылкой на текущий объект. Рассмотрим на примере конструктора:
function createObj(a, b){
this.width = a; // создает свойство именно для конкретного экземпляра
this.height = b;
}var obj_1 = new CreateObj(12, 14);
var obj_2 = new CreateObj(56, 20);
Если бы в конструкторе отсутствовало this, то это были бы созданы глобальные переменные. В нашем случае были созданы свойства для объекта.
Пример: Без конструктора
var human = {
name: «John»,
age: 18,
info: = function(){alert(this.name + » » + this.age)}
};human.info(); // John 18
// Если убрать из метода this, то будет ошибка — в таком объявлении переменные не станут глобальными
Также this используется в атрибуте HTML
Пример:
<button onclick=»this.style.display=’none'»> // Здесь this означает текущий объект DOM button.
Объекты DOM
Любой HTML элемент рассматривается JavaScript как объект. Не важно — строчный или блочный элемент, форма или фрейм.
Пример:
alert(document.getElementById(«p»)); //[object HTMLPElement]
alert(document.getElementById(«h1»)); //[object HTMLH1Element]
alert(document.getElementById(«span»)); //[object HTMLSpanElement]
alert(document.getElementById(«li»)); //[object HTMLLiElement]
alert(document.getElementById(«div»)); //[object HTMLDivElement]
alert(document.getElementById(«header»)); //[object HTMLHeaderElement]
alert(document.getElementById(«button»)); //[object HTMLButtonElement]
alert(document.getElementById(«input»)); //[object HTMLInputElement]
alert(document.body); //[object HTMLBodyElement]
alert(document.images[0]); //[object HTMLImageElement]var div = document.getElementById(«div»); // сохраняем объект div
div.innerHTML = «Hello»; // Теперь в div будет текст
Объекты BOM
JavaScript может манипулировать объектами браузера:
- Location — объект геолокации.
- Screen — объект, содержащий информацию об экране.
- History — объект, содержащий информации об истории просматривания страниц.
- Navigator — информация о браузере.
- Window — главный объект окна браузера, который является вершиной иерархии объектов DOM
Пример:
alert(Screen.height); // выведет ширину экрана
window.open(); // откроет новое окно браузера
JSON для объектов
Иногда для передачи объектов по сети, нужно использовать JSON, который конвертирует объект в строку и делает из строки объект при попадании его на сервер или клиентскую машину.
Пример:
var myObj = {name: «John», age: 31, city: «New York»};
var myJSON = JSON.stringify(myObj); // Получили строку «John 31 New York»// Чтобы конвертировать строку обратно в объект нужно сделать следующее
var myJSON = ‘{«name»:»John», «age»:31, «city»:»New York»}’;
var myObj = JSON.parse(myJSON); // Получили объект из строки
Наследование объектов
В JavaScript поддерживается наследование не только на уровне классов, но и объектов. Создав новый объект, мы можем унаследовать свойства и методы ранее созданных объектов. Наследовать можно как от одного объекта, так и нескольких. Наследование позволяет значительно сократить запись объектов, которые логически связаны. Используются для этого простые конструкции, не требующие повторения одного и того же кода.
Пример:
//конструктор человека
function Human (surname, age) {
this.surname = surname;
this.age = age;
this.go = function(){document.write(this.name + » идет <br/>»);}
this.humanInfo = function(){
document.write(«Name: » + this.surname + «; age: » + this.age);
};
}
Human.prototype.maximumAge = 100; // добавляем в конструктор свойство, определяющее максимальный возраст человека// конструктор сотрудника компании, который будет наследовать функционал человека
function Worker(surname, age, company_name){
Human.call(this, surname, age); // передаем все свойства от человека к работнику, вызовом конструктора, создающего человека
this.company_name = company_name;
this.workerInfo = function(){
document.write(«Name: » + this.surname + «; age: » + this.age + «; company: » + this.company_name);
};
}
Worker.prototype = Object.create(Human.prototype);var John = new Human(«John», 23);
var Edward = new Worker(«Edward», 32, «Yandex»);
John.go();
Edward.go();
John.humanInfo();
Edward.workerInfo();
console.log(Edward.maximumAge);// программа выведет:
Name: John; age: 23;
Name: Edward; age: 32; company: Yandex
Сначала был определен конструктор объектов Human. Затем, чтобы добавить конструктору новое свойство в ходе выполнения программы следует обратиться к его прототипу. В нашем случае это конструкция Human.prototype. Здесь мы добавили ему свойство maximumAge. Сам процесс наследования происходит при создании объекта.
В конструкторе Worker есть такая инструкция, как Human.call(this, surname, age). Она вызывает конструктор объекта Human прямо в конструкторе Worker. Это копирует имена свойств от объекта Human в объект Worker. При этом, метод call() вызывает конструктор в контексте Worker. Данная конструкция попросту сокращает код, не требуя записывать одни и те же свойства несколько раз. При этом, Worker получает и методы, хранящиеся в конструкторе Human.
Также достойна внимания конструкция Worker.prototype = Object.create(Human.prototype). Здесь создается прототип Human и присваивается прототипу Worker. Данная конструкция является гарантией того, что созданные объекты Worker всегда будут наследовать функционал от Human. При этом достаточно добавить свойство или метод через Worker.prototype и оно появится у тех и других создаваемых объектов. То есть, не нужно присваивать прототипам конструкторов свойства по отдельности.
Расширяем объекты посредством prototype
Часто бывает, что в конструктор нужно добавить свойство или метод, который будет принадлежать всем объектам. Для этого достаточно просто написать это в блоке конструктора. Однако часто бывает такая ситуация, когда добавить новое свойство или метода необходимо программным способом. Для этого используется свойство prototype, которым обладает каждый конструктор объектов.
Пример:
function Proto_human(protoName, protoAge) {
this.name = protoName;
this.age = protoAge;
this.show_proto_info = function(){
document.write(«Имя: » + this.name + «; возраст: » + this.age + «<br/>»);
};
};Proto_human.prototype.greetings = function(){ // добавляем в конструктор новый метод, который будет присвоен всем создаваемым объектам
document.write(this.name + » say: ‘Hello!'<br/>»);
};
Proto_human.prototype.someNumber = 250; // добавление в конструктор свойстваvar human1 = new Proto_human(«Ben», 22);
human1.greetings(); // Ben say: Hello!
var human2 = new Proto_human(«Zak», 28);
human2.greetings(); // Zak say: Hello!
console.log(human1.someNumber); // 250
console.log(human2.someNumber); // 250
Стоит отметить, что объекты, которые были созданы до выполнения присвоения свойства или методы прототипу, останутся не изменены. То есть, они не получат нового метода и свойства. Их получат только те объекты, которые будут создаваться после конструкции «Proto_human.prototype.some = value».
Вообще prototype является резервным хранилищем свойств и методов. Когда объект обращается к методу или свойству, добавленного в прототип, то интерпретатор сначала ищет его в самом объекте. Если не находит, то уже обращается к prototype. Если уже и тут его нет, то генерируется ошибка.
Проверка наличия свойства в объекте через оператор in
Оператор in возвращает значение типа boolean, если в объекте есть запрашиваемый метод или свойство. Синтаксис оператора следующий: «имя свойства» in объект. Обратите внимание, что имя свойства заключается в кавычках, а имя объекта без них.
Пример:
var user = {name: «Max», display: function(){alert(this.name);}};
var user_has_name_prop = «name» in user; // проверяем наличие свойства с именем «name»
alert(user_has_name_prop); // true — свойство name есть в user
var user_has_surname_prop = «surname» in user; // проверяем наличие свойства с именем surname
alert(user_has_surname_prop); // false — в user нет свойства или метода под названием surname
Альтернативой данному оператору является метод Obj.hasOwnProperty().
Пример:
var user = {name: «Max», display: function(){alert(this.name);}};
alert(user.hasOwnProperty(name)); // true
alert(user.hasOwnProperty(display)); // true
alert(user.hasOwnProperty(width)); // false
Итог
- Объекты в JavaScript являются структурным типом данных, хранящим свойства и методы.
- Доступ к свойствам осуществляется через имя объекта, оператор «.» имя свойства или метода.
- Свойства и методы не индексируются, как в массиве.
- Объекты можно уничтожить, присвоив переменной значение null — если ссылки на объект две и более, то таким образом уничтожается ссылка, а не объект.
- Объект можно перебирать в цикле.
- Объекты могут быть свойствами других объектов.
- У объектов есть прототип.
- Свойствами объекта могут быть массивы.
- Все объекты получают свойства и методы прототипа — пользовательские объекты наследуют прототип конструктора и Object.prototype.