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

Необходимые знания: Элементарная компьютерная грамотность, базовое понимание HTML и CSS, знакомство с основами JavaScript (см. Первые шаги и Структурные элементы).
Цель: Понимать основу теории перед началом объектно-ориентированного программирования, как это связано с JavaScript ("большинство сущностей являются объектами"), и как начать работу с объектами JavaScript.

Основы объектов

Объект это совокупность связанных данных и/или функциональности(которые обычно состоят из нескольких переменных и функций — которые называются свойства и методы, если они находятся внутри объектов.) Разберём пример, чтобы показать как они выглядят.

Чтобы начать, скопируйте себе наш oojs.html файл. В нём содержится очень мало — <script> элемент для написания в нём исходного кода, <input> элемент для ввода примеров инструкций при отображении станицы, несколько определений переменных, и функция, которая выводит любой код введенный в <p> элемент. Мы будем использовать это как основу для изучения основ синтаксиса объектов.

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

var person = {};

Если Вы введёте person в текстовое поле input и нажмёте Submit code кнопку, должен получиться следующий результат:

[object Object]

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

var person = {
  name: ['Bob', 'Smith'],
  age: 32,
  gender: 'male',
  interests: ['music', 'skiing'],
  bio: function() {
    alert(this.name[0] + ' ' + this.name[1] + ' is ' + this.age + ' years old. He likes ' + this.interests[0] + ' and ' + this.interests[1] + '.');
  },
  greeting: function() {
    alert('Hi! I\'m ' + this.name[0] + '.');
  }
};

После сохранения и обновления, попробуйте ввести что-нибудь следующее в текстовое поле:

person.name[0]
person.age
person.interests[1]
person.bio()
person.greeting()

Теперь внутри объекта есть некоторые данные и функционал, и теперь можно получить доступ к ним с помощью некоторого лёгкого и простого синтаксиса!

Примечание: Если у вас возникли проблемы с применением файла в работе, попробуйте сравнить ваш код с нашей версией — см. oojs-finished.html (также see it running live). Одна из распространенных ошибок, когда вы начинаете с объектами ставить запятую в конце последнего члена — это приводит к ошибке.

Итак что здесь происходит? Объект состоит из нескольких элементов, каждый из которых имеет своё название (пример name и age выше), и значение (пример ['Bob', 'Smith'] и 32). Каждая пара название/значение должны быть разделены запятой, а название и значение в каждом случае разделяются двоеточием :. Синтаксис всегда следует этому образцу:

var objectName = {
  member1Name: member1Value,
  member2Name: member2Value,
  member3Name: member3Value
}

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

Такие объекты называются  литералами объекта (object literal) — мы буквально вписали все содержимое объекта для его создания. Этот способ сильно отличается от объектов реализованных классами, которые мы рассмотрим позже.

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

Точечная нотация (Dot notation)

Выше вы получили доступ к свойствам и методам используя точечную нотация (dot notation).  Имя объекта (person) действует как пространство имен (namespace) — оно должно быть введено первым, для того чтобы получить доступ ко всему что заключено (encapsulated) внутри объекта. Затем вы пишите точку, затем элемент который хотите получить — это может быть именем простого свойства, элементом массива, или вызовом одного из методов объекта, например:

person.age
person.interests[1]
person.bio()

Внутренние пространства имен (Sub-namespaces)

Можно даже сделать значением элемента объекта другой объект. Например, попробуйте изменить значение свойства name с такого

name: ['Bob', 'Smith'],

на такое

name : {
  first: 'Bob',
  last: 'Smith'
},

Здесь мы фактически создаем внутреннее пространство имен (sub-namespace). Это звучит сложно, но на самом деле это не так — для доступа к этим элементам вам нужно сделать один дополнительный шаг через точку. Попробуйте это: 

person.name.first
person.name.last

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

name[0]
name[1]

на

name.first
name.last

Иначе ваши методы больше не будут работать.

Скобочная нотация (Bracket notation)

Существует другой способ получить свойства объекта — использовать скобочную нотацию (bracket notation). Вместо написания этого кода:

person.age
person.name.first

Вы можете использовать следующий

person['age']
person['name']['first']

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

Установка элементов объекта

До сих пор мы рассмастривали только возврат элементов объекта — вы так же можете установить (обновить) значение элемента объекта просто объявив элемент, который вы хотите  установить (используя точечную или скобочную нотацию), например:

person.age = 45;
person['name']['last'] = 'Cratchit';

Попробуйте ввести эти строки, а затем снова верните элементы, чтобы увидеть, как они изменились

person.age
person['name']['last']

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

person['eyes'] = 'hazel';
person.farewell = function() { alert("Bye everybody!"); }

Теперь вы можете проверить ваши новые элементы:

person['eyes']
person.farewell()

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

var myDataName = nameInput.value;
var myDataValue = nameValue.value;

we could then add this new member name and value to the person object like this:

Мы могли бы затем добавить новый элемент и его значение в объект personтаким образом:

person[myDataName] = myDataValue;

Чтобы проверить это, попробуйте добавить следующие строки в свой код, после закрывающей скобки объекта person :

var myDataName = 'height';
var myDataValue = '1.75m';
person[myDataName] = myDataValue;

Теперь сохраните и обновите, и введите следующее в ваше текстовое поле:

person.height

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

Что такое "this"?

You may have noticed something slightly strange in our methods. Look at this one for example:

Вы могли заметить кое-что странное в наших методах. Посмотрите на это, например:

greeting: function() {
  alert('Hi! I\'m ' + this.name.first + '.');
}

Вы, вероятно, задаетесь вопросом, что такое "this"? this  это ключевое слово, ссылающеется на текущий объект, внутри которого написан код — поэтому в нашем случае this  равен  объекту person. Но почему просто не написать person? Как вы увидите в статье Object-oriented JavaScript for beginners (Объектно-ориентированный JavaScript для начинающих), когда мы начинаем создавать конструкторы и т.д., this очень полезен — он всегда будет гарантировать, что используется верное значение когда контекст элемента изменяется (например, два разных экземпляра объекта person  могут иметь разные имена, но захотят использовать свое собственное при приветствии (метод greeting()).

Давайте проиллюстритуем, что мы имеем ввиду с упрощенной парой объектов person :

var person1 = {
  name: 'Chris',
  greeting: function() {
    alert('Hi! I\'m ' + this.name + '.');
  }
}

var person2 = {
  name: 'Brian',
  greeting: function() {
    alert('Hi! I\'m ' + this.name + '.');
  }
}

В этом случае, person1.greeting() выведет "Hi! I'm Chris."; person2.greeting() с другой стороны выведет "Hi! I'm Brian.", хотя код метода одинаковый в обоих случаях. Как мы сказали ранее, this равен объекту, внутри которого находится код — это не очень полезно когда вы пишите литералы объектов вручную, но оно действительно помогает, когда вы генерируете объекты динамически (например используя конструкторы). Это станет понятнее чуть позже.  

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

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

Поэтому, когда вы использовали строковые методы, такие как:

myString.split(',');

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

Когда вы обращались к объектной модели документа (DOM), используя следующие строки:

var myDiv = document.createElement('div');
var myVideo = document.querySelector('video');

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

Тоже самое верно и для многих других встроенных объектов/API которые вы использовали — Array, Math, и т. д.

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

var myNotification = new Notification('Hello!');

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

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

Резюме

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

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

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

 Внесли вклад в эту страницу: DoRightt, superpuper32
 Обновлялась последний раз: DoRightt,