Объектно-ориентированное программирование на сегодняшний день является одной из господствующих парадигм в разработке приложений, и в JavaScript мы также можем использовать все преимущества ООП. В то же время применительно к JavaScript объектно-ориентированное программирование имеет некоторые особенности.
В прошлых темах мы работали с примитивными данными - числами, строками, но данные не всегда представляют примитивные типы. Например, если в нашей программе нам надо описать сущность человека, у которого есть имя, возраст, пол и так далее, то естественно мы не сможем представить сущность человека в виде числа или строки. Нам потребуется несколько строк или чисел, чтобы должным образом описать человека. В этом плане человек будет выступать как сложная комплексная структура, у которого будут отдельные свойства - возраст, рост, имя, фамилия и т.д.
Для работы с подобными структурами в JavaScript используются объекты. Каждый объект может хранить свойства, которые описывают его состояние, и методы, которые описывают его поведение
Есть несколько способов создания нового объекта.
Первый способ заключается в использовании конструктора Object
:
const user = new Object();
В данном случае объект называется user
.
Выражение new Object()
представляет вызов конструктора - функции, создающей новый объект. Для вызова конструктора применяется оператор new. Вызов конструктора фактически
напоминает вызов обычной функции.
Второй способ создания объекта представляет использование фигурных скобок (литеральная нотация):
const user = {};
На сегодняшний день более распространенным является второй способ.
После создания объекта мы можем определить в нем свойства. Чтобы определить свойство, надо после названия объекта через точку указать имя свойства и присвоить ему значение:
const user = {}; user.name = "Tom"; user.age = 26;
В данном случае объявляются два свойства name
и age
, которым присваиваются соответствующие значения. После этого мы можем использовать
эти свойства, например, вывести их значения в консоли:
console.log(user.name); console.log(user.age);
Также можно определить свойства при определении объекта:
const user = { name: "Tom", age: 26 };
В этом случае для присвоения значения свойству используется символ двоеточия, а после определения свойства ставится запятая (а не точка с запятой).
Кроме того, доступен сокращенный способ определения свойств:
const name = "Tom"; const age = 34; const user = {name, age}; console.log(user.name); // Tom console.log(user.age); // 34
В данном случае названия переменных также являются и названиями свойств объекта. И таким образом можно создавать более сложные конструкции:
const name = "Tom"; const age = 34; const user = {name, age}; const teacher = {user, course: "JavaScript"}; console.log(teacher.user); // {name: "Tom", age: 34} console.log(teacher.course); // JavaScript
Методы объекта определяют его поведение или действия, которые он производит. Методы представляют собой функции. Например, определим метод, который бы выводил имя и возраст человека:
const user = {}; user.name = "Tom"; user.age = 26; user.display = function(){ console.log(user.name); console.log(user.age); }; // вызов метода user.display();
Как и в случае с функциями методы сначала определяются, а потом уже вызываются.
Также методы могут определяться непосредственно при определении объекта:
const user = { name: "Tom", age: 26, display: function(){ console.log(this.name); console.log(this.age); } };
Как и в случае со свойствами, методу присваивается ссылка на функцию с помощью знака двоеточия.
Чтобы обратиться к свойствам или методам объекта внутри этого объекта, используется ключевое слово this. Оно означает ссылку на текущий объект.
Также можно использовать сокращенный способ определения методов, когда двоеточие и слово function опускаются:
let 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
Существует также альтернативный способ определения свойств и методов с помощью синтаксиса массивов:
const 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"]
Также можно определить свойства и методы через синтаксис массивов напрямую при создании объекта:
const user = { ["name"]: "Tom", ["age"]: 26, ["display"]: function(){ console.log(user.name); console.log(user.age); } }; user["display"]();
Также следует отметить, что названия свойств и методов объекта всегда представляют строки. То есть мы могли предыдущее определение объекта переписать так:
const user = { "name": "Tom", "age": 26, "display": function(){ console.log(user.name); console.log(user.age); } }; // вызов метода user.display();
С одной стороны, разницы никакой нет между двумя определениями. С другой стороны, бывают случаи, где заключение названия в строку могут помочь. Например, если название свойства состоит из двух слов, разделенных пробелом:
const 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"]();
Только в этом случае для обращении к подобным свойствам и методам мы должны использовать синтаксис массивов.
Синтаксис массивов открывает нам другую возможность - определение имени свойства вне объекта:
const prop1 = "name"; const prop2 = "age"; const tom = { [prop1]: "Tom", [prop2]: 37 }; console.log(tom); // {name: "Tom", age: 37} console.log(tom.name); // Tom console.log(tom["age"]); // 37
Благодая этому, например, можно динамически создавать объекты с произвольными названиями свойств:
function createObject(propName, propValue){ return { [propName]: propValue, print(){ console.log(`${propName}: ${propValue}`); } }; } const person = createObject("name", "Tom"); person.print(); // name: Tom const book = createObject("title", "JavaScript Reference"); book.print(); // title: JavaScript Reference
Выше мы посмотрели, как можно динамически добавлять новые свойства к объекту. Однако также мы можем удалять свойства и методы с помощью оператора delete. И как и в случае с добавлением мы можем удалять свойства двумя способами. Первый способ - использование нотации точки:
delete объект.свойство
Либо использовать синтаксис массивов:
delete объект["свойство"]
Например, удалим свойство:
let 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.
При создании объекта его свойствам могут передаваться значения переменных, констант или динамически вычисляемые результаты функций:
function getSalary(status){ if(status==="senior") return 1500; else return 500; } const name = "Tom"; const age = 37; const person = { name: name, age: age, salary: getSalary()}; console.log(person); // {name: "Tom", age: 37, salary: 500}
Но если названия констант/переменных совпадает с названиями свойств, то можно сократить передачу значений:
const name = "Tom"; const age = 37; const salary = 500; const person = { name, age, salary}; console.log(person); // {name: "Tom", age: 37, salary: 500}
В данном случае объект person автоматически получит свойства, названия которых будут соответствовать названиям констант, а в качестве значений иметь значения этих констант.
То же самое относится к передаче функций методам объекта:
function display(){ console.log(this.name, this.age); } const move = function(place){ console.log(this.name, "goes to", place)}; const name = "Tom"; const age = 37; const salary = 500; const person = { name, age, salary, display, move}; person.display(); // Tom 37 person.move("cinema"); // Tom goes to cinema
В данном случае объект person имеет два метода, которые соответствуют переданным в объект функциям - display()
и move()
.
Стоит отметить, что при такой передаче функций методам объекта, мы по прежнему можем использовать в этих функциях ключевое слово this
для обращения к функциональности объекта.
Однако стоит быть осторожным при передаче лямбд-выражений, поскольку для глобальных лямбд-выражений this будет представлять объект окна браузера:
const move = (place)=>{ console.log(this.name, "goes to", place); console.log(this);}; const name = "Tom"; const person = { name, move}; person.move("cinema"); // goes to cinema
С помощью функции Object.fromEntries() можно создать объект из набора пар ключ-значение, где ключ потом будет представляет название свойства. Например, создадим объект из массивов:
const personData = [ ["name", "Tom"], ["age", 37]]; const person = Object.fromEntries(personData); console.log(person); // {name: "Tom", age: 37} console.log(person.name); // Tom
Здесь объект создается из массива personData, который содержит два подмассива. Каждый подмассив содержит два элемента и фактически представляет пару ключ-значение. Первый элемент представляет ключ, а второй - значение.