Что такое объект в JavaScript?

Объект в JavaScript чем-то похож на реальный объект.
Например, объектом может быть ваш компьютер. Он имеет различные свойства, такие как цвет, размер экрана и множество методов (функций), таких как просмотр Интернета. Компьютеры могут различаться, например, они могут иметь другую операционную систему, например macOS или Linux.
Как и другие типы данных, объекты могут содержать различные значения. Значения записываются парой ключ-значение внутри фигурных скобок. Эти пары ключ-значение называются свойствами объектов. Функции, которые также являются свойствами, называются методами.

Характеристики

Свойства — это пары ключ-значение, которые существуют внутри объекта. Объекты состоят из набора этих различных свойств, которые вы можете добавлять, удалять или изменять (но не все из них). Свойства записываются парами ключ-значение, где ключ — это имя свойства, а значение — один из атрибутов, содержащих информацию о нашем ключе. У свойств есть и другие атрибуты, помимо значения, которое вам сейчас не очень нужно, но полезно знать. Вы можете проверить их, используя Object.getOwnPropertyDescriptor(objectname, «key»).

Методы

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

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

Прототипы

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

Давайте создадим простой объект для визуализации процесса.
Я создам простой объект person со свойством name со значением «Catherine» и методом sayHi, который будет отображать в консоли приветствие при вызове.

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

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

Как вы видите в списке, у нас есть различные свойства, и одно из них — это sayHi, которое мы создали.
Теперь угадайте, какое свойство является прототипом? Это тот, который называется __proto__, так что да, он не совсем прототип. Кроме того, вы также можете найти прототип, выполнив Object.getPrototypeOf(person). Оба способа возвращают один и тот же результат.

Вы также можете консольно зарегистрировать объект человека, и при изучении объекта вы увидите правильность прототипа, написанного как [[Prototype]]: Object. Это ссылка на объект-прототип.

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

Сеть прототипов

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

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

Разверните объект person, чтобы увидеть, что у нас здесь есть. Вы заметите [[Prototype]], который является объектом.

Развернув его, вы увидите различные встроенные свойства объекта.

Если вы посмотрите вниз, то в конце увидите свойство __proto__, которое направит нас к прототипу. Нажмите на нее, и вы снова увидите встроенные свойства.

Затем вы снова увидите другой __proto__ из __proto__. Если вы снова развернете его, на этот раз он покажет null, потому что цепочка прототипов закончилась.

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

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

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

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

Теперь вот каверзный вопрос. Если я не добавлю функцию sayIntro к этому новому объекту YOU, смогу ли я вести консольный лог так же, как с объектом person? Я не добавлял этот метод к нашему новому объекту, поэтому могу ли я использовать то, чего даже не добавлял?

Благодаря цепочке прототипов, да, вы можете.

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

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

Прототипное наследование

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

Как JavaScript ищет свойство

Представьте, что мы делаем то же самое, что делали ранее, когда искали свойство объекта (человека) и вызывали sayHi. Когда мы ищем свойство, первым местом, с которого начинается поиск, является сам объект. Если ничего не найдено, поиск не заканчивается и переходит к прототипу. Если свойство снова не найдено, поиск продолжается до прототипа прототипа. В конце концов, если ничего не найдено, возвращается значение undefined, потому что ничего не найдено.

Конструктор

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

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

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

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

С другой стороны, изменение свойств, как описано выше, не всегда является хорошим решением, потому что что, если у вас есть объект с кучей различных свойств? Добавление вручную заняло бы много времени.
Что, если вы создали объект пользователя, например, с именем пользователя и адресом электронной почты? Каждый раз, когда кто-то регистрируется, вы не собираетесь вручную писать каждую деталь. Или что, если у меня есть 20 или 30 различных полей информации? Вот почему мы можем расширить конструктор и добавить некоторые аргументы в функцию.
Аргументы — это значения, которые мы передаем в функцию. Я собираюсь воссоздать этот код еще раз, но на этот раз с аргументами.

Выглядит намного короче, быстрее и красивее! Давайте разберемся, что здесь происходит.
В функции Person я добавил такие параметры, как имя, возраст и страна, но они пока не являются фактическими значениями. Параметры похожи на пустые коробки, ожидающие получения значения. Когда я начинаю создавать экземпляр ME, я также передаю аргументы функции, но на этот раз с конкретными значениями. Функция-конструктор получит эту информацию и создаст новый объект с этими конкретными значениями! Теперь вы можете просто использовать этот конструктор и передавать значения вновь зарегистрированных пользователей вместо того, чтобы вручную добавлять каждую часть информации.

это ключевое слово

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

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

это в методе

Если вы читали, то заметили, что я уже использовал ключевое слово this в методе объекта. Когда мы используем this в методе объекта, он ссылается на этот объект (где находится метод).

Если мы не укажем, откуда этот метод получает значение, и оно не находится внутри метода, он просто выдаст ошибку undefined.

Еще один хороший способ проверить, к какому объекту относится this, — просто войти в консоль. Если вы сделаете это в методе, он покажет вам объект, в котором находится метод.

this в функции

Когда мы используем ключевое слово this в обычной функции (не в методе), оно относится к глобальному объекту. Глобальный объект — это объект, существующий в глобальной области видимости. Проще говоря, глобальная область видимости создается, когда ваш файл скрипта запускается в браузере, чтобы прочитать все, что у вас есть в этом файле. Это самое верхнее поле, которое содержит всю информацию. Например, если вы создадите какие-то переменные в самом начале файла скрипта, они будут находиться в глобальной области видимости.
Рекомендую прочитать этот пост, чтобы понять, как работает прицел.

Это в функции (строгий режим)

Строгий режим в JavaScript — это более строгий способ обработки JavaScript, который очень чувствителен к ошибкам, если вы не знаете на 100%, что делаете. Чтобы включить строгий режим, вам просто нужно написать использовать строгий режим в самом верху вашего файла сценария. Я не буду рассказывать, что именно он делает, но вы можете прочитать об этом здесь.
В строгом режиме ключевое слово this больше не относится к глобальному объекту, а вместо этого возвращает значение undefined.

Только это

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

Зачем вам это вообще нужно, тогда отдельно от всего? Поскольку объект окна имеет различные встроенные свойства, которые вы можете использовать.

this в обработчиках событий

События в JavaScript — это взаимодействия, зафиксированные в элементе HTML, которые мы можем использовать для управления ими. Допустим, кто-то нажимает кнопку, мы можем зафиксировать это действие и что-то сделать в ответ на это нажатие. При использовании this с обработчиком событий ключевое слово this относится к элементу HTML, по которому был сделан щелчок.

Это с вызовом, применением и связыванием методов

В JavaScript у нас есть три нативных метода call, apply и bind, которые могут изменить поведение ключевого слова this.

Метод вызова

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

Если вы внимательно все прочитали, то помните, что this в методах относится к объекту, на котором он находится. Объект person имеет свое имя и фамилию, но когда мы используем call, ключевое слово this будет относиться не к объекту person, а к тому, который мы передаем методу call.

Использование аргументов с методом вызова

Метод call также отлично работает с аргументами. Если основной объект, из которого вы получаете метод, имеет некоторые аргументы, вы также можете передать обновленные аргументы. Давайте немного изменим тот же самый объект.

Если есть какие-то аргументы, которые вам нужно передать, но вы этого не делаете, он все равно покажет вам строку результата, но не вставленные аргументы станут неопределенными.

Метод применения

Метод применения делает то же самое, что и метод вызова. Давайте проверим это.

Использование аргументов с методом вызова

Хотя есть небольшая разница, когда дело доходит до использования аргументов. Вместо передачи списка аргументов вам нужно передать массив. Все просто, вот и вся разница.

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

Метод привязки

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

Потеря this в обратных вызовах

Знаете ли вы, что вы можете использовать это ключевое слово в обратных вызовах? Если вы еще не читали, обязательно сначала прочитайте про обратные вызовы, если вы не знаете, что это такое.

Вот простой пример.

this теряет свой контекст и вместо этого показывает undefined.
Чтобы решить эту проблему, нам нужно вернуться к теме поведения this в функции:

«Когда мы используем ключевое слово this в обычной функции (не в методе), оно относится к глобальному объекту. Глобальный объект — это объект, существующий в глобальной области видимости. Проще говоря, глобальная область видимости создается, когда ваш файл скрипта запускается в браузере, чтобы прочитать все, что у вас есть в этом файле. Это самое верхнее поле, которое содержит всю информацию. Например, если вы создадите какие-то переменные в самом начале вашего файла скрипта, они будут находиться в глобальной области видимости».

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

Один из быстрых способов решить эту проблему — обернуть метод функцией и вызвать ее. Функция, вокруг которой мы ее обернули, не имеет собственного this и просто выполняет метод, который мы ей передаем.

Другое решение — использовать привязку метода и привязать метод обратно к объекту, чтобы он не терял своего контекста.

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

Аксессоры (геттеры и сеттеры)

Средства доступа JavaScript, такие как геттеры и сеттеры, — это методы, используемые для получения и установки значений.
Прежде чем идти дальше, давайте быстро разберемся, что такое инкапсуляция и как она связана с этими средствами доступа.
В программировании инкапсуляция означает сохранение и хранение данных таким образом, который делает более ограниченным доступ к этим данным. напрямую. Это способ скрыть данные, чтобы предотвратить их раскрытие. Иногда есть тип данных, которые слишком важны, и легкий доступ делает их более уязвимыми для изменений. Вот почему инкапсуляция — отличный способ защитить эти данные.
Именно здесь в игру вступают методы доступа, поскольку они помогают избежать перезаписи важных данных, а также проверки данных перед их сохранением.

Создание геттеров и сеттеров

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

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

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

Если по какой-либо причине вы решите использовать обычные функции вместо получения или установки ключевых слов, есть способ уменьшить вероятность ошибок.
Вы можете сделать свойства внутри геттеров и сеттеров доступными только для чтения. Свойство только для чтения означает, что вы не можете перезаписать или переназначить его, поэтому оно доступно только для чтения. Помните, я показывал вам атрибуты свойств? Для атрибута writable установлено значение true по умолчанию, что означает, что нам нужно установить для него значение false, чтобы сделать свойство доступным только для чтения.

Для этого мы можем использовать Object.defineProperties.

Защита данных в геттерах и сеттерах

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

Чтобы защитить данные, вы должны использовать знание области видимости в JavaScript! Если вы еще этого не сделали, обязательно прочитайте больше о контексте и объеме выполнения, пока не продолжите дальше.

Объем функции

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

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

Область действия блока

Та же логика будет работать, если мы поместим код в область блока (заключим его в фигурные скобки). Но в этом случае я также определяю объект person с помощью var, чтобы он был доступен за пределами этого блока.

Обратите внимание, что в этом случае мы не использовали какие-либо операторы if или while для использования фигурных скобок, это делается для того, чтобы данные оставались «скрытыми».

Какой из них лучший? Я бы сказал, что первое, но это действительно зависит от ситуации, с кем вы работаете, как построена логика кода и от ваших знаний, если вы работаете над новым или личным проектом.

Итерация по объектам

Итерируемый объект в JavaScript — это структура данных, имеющая механизм, позволяющий последовательно обращаться к ее элементам. Другими словами, это возможность зацикливаться на элементах.
Когда дело доходит до объектов, вы не можете просто перебрать их с помощью существующих циклов, таких как for или for..of. Вместо этого вы можете перебирать свойства объекта, используя Object.keys, или записи, используя Object.entries.

Чтобы пройтись по свойствам, вы можете использовать цикл for…of, в котором вы сначала будете перебирать ключи объекта, а затем вы можете использовать этот ключ для доступа к значению, как я сделал ниже. Мой ключ будет идентификатором переменной, а доступ к значению будет осуществляться с помощью yourObject[theKey], productId[id] в моем случае, который я сохранил в отдельной переменной.

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

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

Разрушение

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

По старому

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

Современный способ

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

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

Псевдонимы

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

Я заменил каждое имя ключа другим именем, поэтому, если у вас его нет, вы можете заменить его чем-то другим.

Остальной оператор

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

Оператор rest можно использовать, даже если у вас остался только один элемент или если у вас его нет. Затем он вернет пустой объект.

Значения по умолчанию

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

Как видите, я добавил свойство onion в деструктуризацию, хотя его еще нет в объекте recipe. Я добавил значение по умолчанию 100 г, поэтому, если лук не появится с каким-либо значением в объекте, он не выдаст никакой ошибки, и будет использоваться значение по умолчанию.

Встроенные объекты

JavaScript имеет различные встроенные объекты. Встроенный означает, что это предопределенный объект, который уже существует, поэтому вы не создаете его с нуля. String, Array или Date — все это встроенные объекты JavaScript, которые часто упоминаются как стандартные встроенные объекты. Я не буду сейчас подробно описывать каждый из них, но если вам интересно, вы можете прочитать об этом подробнее здесь.

Стандартные объекты по категориям

1. Свойства значений
2. Свойства функций
3. Основные объекты
4. Обработка текста
5. Объекты ошибок
6. Числа и даты
7 Интернационализация
8. Индексированные коллекции
9. Отражение
10. Управление памятью
11. Структурированные данные
12. Коллекции с ключами
13. Управление объектами абстракции

Краткое содержание

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