Kwert-soft.ru

IT Софт для ПК
0 просмотров
Рейтинг статьи
1 звезда2 звезды3 звезды4 звезды5 звезд
Загрузка...

Javascript get set

Свойства объектов — геттеры и сеттеры

Свойства объектов — геттеры и сеттеры

Здравствуйте! Продолжаем разбираться со свойствами объекта и в этом уроке рассмотрим 2 типа свойств объекта — геттеры и сеттеры.

Первый тип это свойства-данные или геттеры. Все свойства, которые мы использовали до текущего момента, были свойствами-данными.

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

Геттеры и сеттеры

Свойства-аксессоры представлены методами: «геттер» – для чтения и «сеттер» – для записи. При объявлении объекта они обозначаются get и set:

Геттер срабатывает, когда obj.propName читается, сеттер – когда собственно значение присваивается.

Например, у нас есть объект user со свойствами name и surname:

А теперь добавим свойство объекта fullName для полного имени, которое в нашем случае «John Smith». Само собой, мы не хотим дублировать уже имеющуюся информацию, так что реализуем его при помощи аксессора:

Снаружи свойство-аксессор будет выглядеть как обычное свойство. В этом и заключается смысл свойств-аксессоров. Мы не вызываем user.fullName как функцию, а читаем как обычное свойство: геттер выполнит всю работу за кулисами.

Давайте исправим это, добавив сеттер для user.fullName:

В итоге мы получили «виртуальное» свойство fullName. Его можно прочитать и изменить, но по факту его нет.

Нет поддержки для delete

При попытке удалить свойство-аксессор оператором delete будет ошибка.

Дескрипторы свойств доступа

Дескрипторы свойств-аксессоров отличаются от «обычных» свойств-данных.

Свойства-аксессоры не имеют value и writable, но взамен предлагают функции get и set.

То есть, дескриптор аксессора может иметь:

  • get – функция без аргументов, которая сработает при чтении свойства,
  • set – функция, принимающая один аргумент, вызываемая при присвоении свойства,
  • enumerable – то же, что и для свойств-данных,
  • configurable – то же, что и для свойств-данных.

Например, для создания аксессора fullName при помощи defineProperty мы можем передать дескриптор с использованием get и set:

Ещё раз заметим, что свойство объекта может быть либо свойством-аксессором (с методами get/set), либо свойством-данным (со значением value).

При попытке указать и get, и value в одном дескрипторе будет ошибка:

Умные геттеры/сеттеры

Геттеры/сеттеры можно использовать как обёртки над «реальными» значениями свойств, чтобы получить больше контроля над операциями с ними.

Например, если надо запретить устанавливать короткое имя для user, мы можем использовать сеттер name для проверки, а само значение хранить в отдельном свойстве _name:

Таким образом, само имя хранится в _name, доступ к которому производится через геттер и сеттер.

Технически, код всё ещё может получить доступ к имени напрямую с помощью user._name, но существует известное соглашение о том, что свойства, которые начинаются с символа «_», являются внутренними, и к ним не следует обращаться из-за пределов объекта.

Использование для совместимости

У аксессоров есть интересная область применения – они позволяют в любой момент взять «обычное» свойство и изменить его поведение, поменяв на геттер и сеттер.

Например, представим, что мы начали реализовывать объект user, используя свойства-данные имя name и возраст age:

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

Что нам делать со старым кодом, который использует свойство age?

Мы можем попытаться найти все такие места и изменить их, но это отнимает время и может быть невыполнимо, если код используется другими людьми. И кроме того, age – это отличное свойство для user, верно?

Давайте его сохраним.

Добавление геттера для age решит проблему:

Теперь старый код тоже работает, и у нас есть отличное дополнительное свойство!

Если вы нашли ошибку, пожалуйста, выделите фрагмент текста и нажмите Ctrl+Enter.

Геттеры и сеттеры в Javascript

Javascript — очень изящный язык с кучей интересных возможностей. Большинство из этих возможностей скрыты одним неприятным фактором — Internet Explorer’ом и другим дерьмом, с которым нам приходится работать. Тем не менее, с приходом мобильных телефонов с актуальными браузерами и серверного JavaScript с нормальными движками эти возможности уже можно и нужно использовать прям сейчас. Но по привычке, даже при программировании для node.js мы стараемся писать так, чтобы оно работало в IE6+.

В этой статье я расскажу про интересный и не секретный способ указывать изящные геттеры и сеттеры и немножко покопаемся в исходниках Mootools. Частично это информация взята из статьи John Resig, частично лично мой опыт и эксперименты.

Читать еще:  Java метод append

Стандартные геттеры

Что такое Геттеры и Сеттеры, надеюсь знают все. Обычно и кроссбраузерно это выглядит так:

Можно пойти дальше и написать более изящный вариант:

Нативные геттеры/сеттеры

Но есть более удобный способ, который работает во всех серверных движках и современных браузерах, а именно Firefox, Chrome, Safari3+, Opera9.5+ — задание сеттера и геттера для свойства так, чтобы продолжать обращатся к свойству, как свойству. У такого подхода есть несколько преимуществ:
1. Более изящная запись. Представим ORM:

2. Если апи, которое базируется на свойствах уже есть и его нельзя менять (а очень нужно).

Есть два способа задать такой геттер/сеттер:

Через объект:

Через методы __defineGetter__ и __defineSetter__:

Определяем поддержку браузером

Из этого можно получить лёгкий способ определения, поддерживает ли браузер геттеры или не поддерживает:

Как быть с наследованием?

Получить саму функцию геттера или сеттера можно через методы .__lookupGetter__ и .__lookupSetter__ .

Таким образом нашему target передадутся не значения родительского source, а функции-геттеры/сеттеры.

Что следует помнить

* Для каждого свойства вы можете установить только один геттер и/или сеттер. Не может быть два геттера или сеттера
* Единственный способ удалить геттер или сеттер — это вызвать delete object[name]; . Эта команда удаляет и геттер и сеттер, потому если вы хотите удалить что-то одно, а другое — оставить, надо сначала сохранить его, а после удаления — снова присвоить
* Когда вы используете __defineGetter__ или __defineSetter__ он просто тихонько перезаписывает предыдущий геттер или сеттер и даже удаляет само свойство с таким именем.
* Проверить, поддерживает ли ваш браузер геттеры и сеттеры можно с помощью простого сниппета:

MooTools

Мутулз не поддерживает по-умолчанию такую возможность. И, хотя я уже предложил патч, мы можем с лёгкостью (слегка изменив исходники) заставить его понимать геттеры и сеттеры.
Итак, какая наша цель?

Более того, в классах унаследованных через Implements и Extends тоже должны работать геттеры и сеттеры родительского класса. Все наши действия будут происходить в файле [name: Class] внутри анонимной функции.
Во-первых, внутри функции, в самом верху, определим функцию, которая перезаписывает только геттеры и сеттеры. И хотя мы отказалась от устаревших браузеров — стоит застраховаться.

Конечно, если наш скрипт с такими геттерами попадёт в устаревший браузер, то он просто упадёт, но это страховка от того, чтобы кто-то случайно не взял этот файл и не прицепил его к себе на сайт, а потом недоумевал, что такое с ишаком.
Мы видим, что если __lookupGetter__ не поддерживается, то функция просто ничего не сделает.

Теперь заставляем работать getterы и setterы во время создания класса и наследования (Extends). Для этого:

Отдельным движением надо реализовать наследование геттеров и сеттеров от примесей (Implements). Для этого надо найти встроенные Мутаторы и добавить всего одну строку:

Все, теперь сеттеры и геттеры реализуются и мы с лёгкостью можем их наследовать и использовать. Наслаждайтесь)

Property getters and setters

There are two kinds of properties.

The first kind is data properties. We already know how to work with them. All properties that we’ve been using until now were data properties.

The second type of properties is something new. It’s accessor properties. They are essentially functions that work on getting and setting a value, but look like regular properties to an external code.

Getters and setters

Accessor properties are represented by “getter” and “setter” methods. In an object literal they are denoted by get and set :

The getter works when obj.propName is read, the setter – when it is assigned.

For instance, we have a user object with name and surname :

Now we want to add a fullName property, that should be «John Smith» . Of course, we don’t want to copy-paste existing information, so we can implement it as an accessor:

From outside, an accessor property looks like a regular one. That’s the idea of accessor properties. We don’t call user.fullName as a function, we read it normally: the getter runs behind the scenes.

As of now, fullName has only a getter. If we attempt to assign user.fullName= , there will be an error:

Читать еще:  String compareto java

Let’s fix it by adding a setter for user.fullName :

As the result, we have a “virtual” property fullName . It is readable and writable.

Accessor descriptors

Descriptors for accessor properties are different from those for data properties.

For accessor properties, there is no value or writable , but instead there are get and set functions.

That is, an accessor descriptor may have:

  • get – a function without arguments, that works when a property is read,
  • set – a function with one argument, that is called when the property is set,
  • enumerable – same as for data properties,
  • configurable – same as for data properties.

For instance, to create an accessor fullName with defineProperty , we can pass a descriptor with get and set :

Please note that a property can be either an accessor (has get/set methods) or a data property (has a value ), not both.

If we try to supply both get and value in the same descriptor, there will be an error:

Smarter getters/setters

Getters/setters can be used as wrappers over “real” property values to gain more control over operations with them.

For instance, if we want to forbid too short names for user , we can have a setter name and keep the value in a separate property _name :

So, the name is stored in _name property, and the access is done via getter and setter.

Technically, external code is able to access the name directly by using user._name . But there is a widely known convention that properties starting with an underscore «_» are internal and should not be touched from outside the object.

Using for compatibility

One of the great uses of accessors is that they allow to take control over a “regular” data property at any moment by replacing it with a getter and a setter and tweak its behavior.

Imagine we started implementing user objects using data properties name and age :

…But sooner or later, things may change. Instead of age we may decide to store birthday , because it’s more precise and convenient:

Now what to do with the old code that still uses age property?

We can try to find all such places and fix them, but that takes time and can be hard to do if that code is used by many other people. And besides, age is a nice thing to have in user , right?

Adding a getter for age solves the problem:

Дескрипторы, геттеры и сеттеры свойств

Материал на этой странице устарел, поэтому скрыт из оглавления сайта.

Более новая информация по этой теме находится на странице https://learn.javascript.ru/object-properties.

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

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

Дескрипторы в примерах

Основной метод для управления свойствами – Object.defineProperty.

Он позволяет объявить свойство объекта и, что самое главное, тонко настроить его особые аспекты, которые никак иначе не изменить.

obj Объект, в котором объявляется свойство. prop Имя свойства, которое нужно объявить или модифицировать. descriptor Дескриптор – объект, который описывает поведение свойства.

В нём могут быть следующие поля:

  • value – значение свойства, по умолчанию undefined
  • writable – значение свойства можно менять, если true . По умолчанию false .
  • configurable – если true , то свойство можно удалять, а также менять его в дальнейшем при помощи новых вызовов defineProperty . По умолчанию false .
  • enumerable – если true , то свойство просматривается в цикле for..in и методе Object.keys() . По умолчанию false .
  • get – функция, которая возвращает значение свойства. По умолчанию undefined .
  • set – функция, которая записывает значение свойства. По умолчанию undefined .

Чтобы избежать конфликта, запрещено одновременно указывать значение value и функции get/set . Либо значение, либо функции для его чтения-записи, одно из двух. Также запрещено и не имеет смысла указывать writable при наличии get/set -функций.

Далее мы подробно разберём эти свойства на примерах.

Обычное свойство

Два таких вызова работают одинаково:

Читать еще:  Docker java 8

Оба вызова выше добавляют в объект user обычное (удаляемое, изменяемое, перечисляемое) свойство.

Свойство-константа

Для того, чтобы сделать свойство неизменяемым, изменим его флаги writable и configurable :

Заметим, что без use strict операция записи «молча» не сработает. Лишь если установлен режим use strict , то дополнительно сгенерируется ошибка.

Свойство, скрытое для for…in

Встроенный метод toString , как и большинство встроенных методов, не участвует в цикле for..in . Это удобно, так как обычно такое свойство является «служебным».

К сожалению, свойство toString , объявленное обычным способом, будет видно в цикле for..in , например:

Мы бы хотели, чтобы поведение нашего метода toString было таким же, как и стандартного.

Object.defineProperty может исключить toString из списка итерации, поставив ему флаг enumerable: false . По стандарту, у встроенного toString этот флаг уже стоит.

Обратим внимание, вызов defineProperty не перезаписал свойство, а просто модифицировал настройки у существующего toString .

Свойство-функция

Дескриптор позволяет задать свойство, которое на самом деле работает как функция. Для этого в нём нужно указать эту функцию в get .

Например, у объекта user есть обычные свойства: имя firstName и фамилия surname .

Создадим свойство fullName , которое на самом деле является функцией:

Обратим внимание, снаружи fullName – это обычное свойство user.fullName . Но дескриптор указывает, что на самом деле его значение возвращается функцией.

Также можно указать функцию, которая используется для записи значения, при помощи дескриптора set .

Например, добавим возможность присвоения user.fullName к примеру выше:

Указание get/set в литералах

Если мы создаём объект при помощи синтаксиса < . >, то задать свойства-функции можно прямо в его определении.

Для этого используется особый синтаксис: get свойство или set свойство .

Например, ниже объявлен геттер-сеттер fullName :

Да здравствуют get/set!

Казалось бы, зачем нам назначать get/set для свойства через всякие хитрые вызовы, когда можно сделать просто функции с самого начала? Например, getFullName , setFullName …

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

Например, в начале разработки мы используем обычные свойства, например у User будет имя name и возраст age :

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

…Но рано или поздно могут произойти изменения. Например, в User может стать более целесообразно вместо возраста age хранить дату рождения birthday :

Что теперь делать со старым кодом, который выводит свойство age ?

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

Добавление get -функции age позволяет обойти проблему легко и непринуждённо:

Заметим, что pete.age снаружи как было свойством, так и осталось. То есть, переписывать внешний код на вызов функции pete.age() не нужно.

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

Другие методы работы со свойствами

Позволяет объявить несколько свойств сразу:

Возвращают массив – список свойств объекта.

Object.keys возвращает только enumerable -свойства.

Object.getOwnPropertyNames – возвращает все:

Возвращает дескриптор для свойства obj[prop] .

Полученный дескриптор можно изменить и использовать defineProperty для сохранения изменений, например:

…И несколько методов, которые используются очень редко:

Object.preventExtensions(obj) Запрещает добавление свойств в объект. Object.seal(obj) Запрещает добавление и удаление свойств, все текущие свойства делает configurable: false . Object.freeze(obj) Запрещает добавление, удаление и изменение свойств, все текущие свойства делает configurable: false, writable: false . Object.isExtensible(obj) Возвращает false , если добавление свойств объекта было запрещено вызовом метода Object.preventExtensions . Object.isSealed(obj) Возвращает true , если добавление и удаление свойств объекта запрещено, и все текущие свойства являются configurable: false . Object.isFrozen(obj) Возвращает true , если добавление, удаление и изменение свойств объекта запрещено, и все текущие свойства являются configurable: false, writable: false .

Задачи

Добавить get/set-свойства

Вам попал в руки код объекта User , который хранит имя и фамилию в свойстве this.fullName :

Имя и фамилия всегда разделяются пробелом.

Сделайте, чтобы были доступны свойства firstName и lastName , причём не только на чтение, но и на запись, вот так:

Важно: в этой задаче fullName должно остаться свойством, а firstName/lastName – реализованы через get/set . Лишнее дублирование здесь ни к чему.

Ссылка на основную публикацию
Adblock
detector