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

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

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

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

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

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

var person = {};

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

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] + '.');
  }
};

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

person.name
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']

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

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

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

person['eyes']
person.farewell()

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

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

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

person[myDataName] = myDataValue;

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

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

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

person.height

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

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

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

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

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

Давайте проиллюстритуем, что мы имеем ввиду с упрощенной парой объектов 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 как отдельные переменные и функции, это было неэффективно, и мы бы рисковали столкнуться с другими переменными и функциями с такими же именами. Объекты позволяют нам безопасно хранить информацию в своем собственном блоке, вне опасности.

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

 

В этом модуле

 

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

Метки: 
Внесли вклад в эту страницу: genesky, boxa6, mdnwebdocs-bot, injashkin, NooNoo1337, Dusmatoff, KoS57, slychai85, Jony_e, slowpokex, DoRightt, superpuper32
Обновлялась последний раз: genesky,