0:00
Добрый день.
Сегодня мы рассмотрим еще один способ создания методов у объектов.
Этот метод позволяет сделать более тонкую и более глубокую настройку того,
как будут себя вести наши методы или наши свойства.
У нас есть тот же объект panda с методом valueOf и с методом toString,
что и на прошлых занятиях.
Мы передаем его внутрь метода Object.keys, про который я уже рассказывал,
и получаем массив, состоящий из двух строк: строки 'valueOf'
и строки 'toString' — именами ключей, которые созданы у этого объекта.
Теперь создадим пустой объект,
и точно так же передадим его внутрь функции Object.keys.
И в этом случае мы получим пустой массив, что, в общем-то, предсказуемо,
ведь объект пустой, и у него нет никаких методов, которые бы мы объявили.
Теперь применим оператор typeof к нашему методу valueOf нашего объекта panda.
И мы получим то, что это — функция, и то, что она вызываемая.
Что, в общем-то предсказуемо — мы же сами задекларировали его, как метод объекта.
И сделаем то же самое для нашего пустого объекта, у которого,
по нашему мнению, нет ни единого метода.
Точно так же запросим у него метод valueOf и передадим его оператору typeof.
Но при этом мы снова получаем то, что это функция.
Как?
Но ведь мы же не видим ее в списке методов.
Но при этом она есть и она вызываемая.
Мы можем пользоваться ей из нашего кода.
Далее мы рассмотрим то, как создавать такие методы, которые, с одной стороны,
не видны снаружи, а с другой стороны — по-прежнему вызываемы,
и вы можете ими пользоваться.
Для этого есть специальный метод у объекта Object, который называется defineProperty.
Как вы уже, наверное, заметили, у объекта Object есть множество полезных методов,
и это далеко не последний, который будет рассмотрен сегодня.
Он принимает в себя несколько параметров.
Первым параметром он принимает, собственно, наш объект,
с которым мы хотим работать.
А вторым параметром он принимает имя ключа,
которое будет создано внутри этого объекта.
А третий параметр — это так называемый объект конфигурирования.
И одним из его ключей может являться ключ value, который будет говорить о том,
какое значение или какая функция будет соответствовать этому свойству у объекта.
В нашем случае мы хотим сделать функцию, поэтому мы записываем,
что значение value будет функция.
И важный момент, обратите внимание: у него есть,
кроме value, еще три параметра.
О двух мы поговорим чуть позже, сейчас нам важен параметр enumerable,
который мы явно выставляем false.
Он как раз и говорит о том, будет видна функция снаружи или нет.
В нашем случае мы хотим создать скрытую функцию,
поэтому мы говорим: enumerable — false.
И точно так же делаем для объекта toString.
Таким образом мы сможем повторить поведение такое же, как у пустого объекта.
У нас будут функции, они будут вызываемы, но они не будут видны,
мы не будем их видеть в Object.keys, и не сможем по ним итерироваться.
Также существует метод defineProperties,
который позволяет задать свойство, но уже не одно, а сразу же несколько.
Подробнее о нем вы можете прочитать по ссылке.
Давайте теперь подробнее остановимся на параметре конфигурирования writable.
Создадим пустой объект, в котором мы будем хранить твит.
И с помощью defineProperty для этого объекта твита создадим новое свойство,
которое будет содержать текст твита.
Как вы видите, в данном случае в value мы передаем не функцию,
а примитив в виде строки.
И явно указываем, что свойство writable выставлено в значение false.
После этого воспользуемся еще одним методом объекта Object,
который называется getOwnPropertyDescriptor,
который позволяет нам получить информацию о личном свойстве объекта,
которое было создано тем или иным способом,
которое было выставлено для него, как ссылка, или которое было к нему присвоено.
В данном случае мы его создали через defineProperty.
В него передается объект, который мы хотим проверить,
и имя свойства, по которому мы хотим получить необходимую для нас информацию.
И мы видим, что у нас есть свойство, у которого есть объект value,
который представляет собой строку, и параметр writable выставлен в false,
параметр enumerable выставлен в false и параметр configurable выставлен в false.
Это поведение по умолчанию.
Обратимся к этому текстовому полю, как к обычному свойству нашего объекта.
Мы получаем текст нашего твита.
Теперь попытаемся присвоить этому свойству новое значение.
Все пройдет хорошо, не будет выброшено какого-либо исключения,
и код не вызовет ошибку.
Но при повторном обращении к свойству мы увидим, что значение не изменилось,
и мы по-прежнему получаем предыдущее значение свойства,
которое было выставлено при вызове метода defineProperty.
Таким образом мы сделали поле неизменяемым.
Важный момент, на который стоит обратить внимание — это то,
что по умолчанию все поля — writable,
configurable, enumerable — выставляются со значением false.
То есть вы можете вызывать defineProperty, передавать в него в
качестве конфигурационного объекта только объект, содержащий поле value,
и тогда все остальные значения будут явно выставлены false.
Но я бы рекомендовал вам декларировать важные для вас поля,
даже если это значение false, которое принимается по умолчанию, в вашем коде,
чтобы люди, которые пользуются вашей функцией или вашим кодом, понимали,
что вы намеренно выставили именно это значение в именно это состояние.
Теперь смотрим configurable.
Точно тот же самый объект твита,
создаем у него то же самое текстовое поле, но говорим ему configurable: false.
Проверяем через getOwnPropertyDescriptor,
что действительно наши параметры выставились,
и объект и свойства получилось именно такими, как мы и планировали,
считываем это свойство, видим, что мы получаем из него текст,
проверяем, что оно существует,
через метод hasOwnProperty, который мы рассмотрели в предыдущей лекции.
Видим, что да, он возвращает true, такое свойство у объекта действительно есть.
А теперь пытаемся с помощью оператора delete удалить это свойство.
И мы получаем false.
Это важный момент.
Оператор delete всегда возвращает true или false в зависимости от того,
успешная или неуспешная была попытка удаления свойства.
В нашем случае попытка неуспешная, так как мы сказали, что свойство неконфигурируемо.
Удаление является частью конфигурирования свойства,
поэтому мы не смогли его удалить.
И delete вернул false.
Обращаемся к свойству — свойство по-прежнему доступно.
Проверяем его наличие — свойство есть.
Как вы видите, операция удаления ничего не произвела
и ничего не поменяла у нашего объекта.
Заморозка.
В мире Бэтмена есть такой персонаж — мистер Фриз,
который был одним из его врагов и замораживал все и вся вокруг себя.
Ну, судя по всему, разработчикам Javascript тоже нравился мир Бэтмена,
и они решили, что будет интересно и понятно для пользователей
назвать часть методов с помощью этого имени — freeze.
Так что поговорим о заморозке.
Например, у нас есть объект твита, мы получаем его из какого-то внешнего API.
И при этом мы можем его изменять в нашем коде.
Но допустим, мы этого не хотим.
Мы хотим, чтобы то, что нам прислали, оставалось в неизменном виде.
Именно для этого и была сделана заморозка.
У объекта Object есть еще один полезный метод.
Он называется isFrozen.
И он проверяет для переданного в качестве аргумента в него функции,
массива или объекта, является ли она замороженной.
В нашем случае — нет, потому что это обычный объект,
который был задекларирован обычным способом.
И isFrozen вернет false.
И если мы проверим у этого объекта любой из параметров, например,
количество лайков, с помощью getOwnPropertyDescriptor, мы увидим, что
это значение, которое представляет из себя целое число 16, что оно writable: true.
то есть мы можем его отредактировать, так как это обычное свойство, оно изменяемо.
Оно конфигурируемое,
так как configurable выставлен тоже в true, мы можем его удалить.
И оно enumerable: true, то есть мы можем увидеть это свойство
через Object.Keys или при итерировании по нашему объекту.
А теперь вызовем метод Object.freeze и передадим в него этот объект.
Мы заморозим наш объект.
Допустим, мы не хотим, чтобы что-то менялось при использовании внешнего API.
А вот теперь с помощью метода isFrozen проверяем, заморожен ли наш объект.
И теперь да, мы получаем true, наш объект заморозился.
Проверяем теперь, что из себя представляет значение
свойства likes с помощью getOwnPropertyDescriptor.
И мы видим, что параметр writable стал false, то есть поле стало нередактируемое,
и параметр configurable стал тоже false, то есть поле стало неизменяемым.
При этом параметр enumerable остался true, как и был.
Заморозка никаким образом не влияет на то, видимо поле или невидимо.
Этим мы уже управляем лично сами.
И теперь попробуем, например, приравнять этому свойству новое значение,
а потом считать, и мы увидим, что значение не поменялось.
Изменения не произошло: поле неизменяемое.
Попытаемся удалить и получим false.
В этой лекции мы рассмотрели еще один способ декларации методов у объектов.
Более гибкий, позволяющий нам настроить поведение наших свойств,
методов, неважно на то, как мы этого хотим: будут они видимые,
будут они невидимые, будут ли они изменяемыми или нет.
А также мы познакомились с тем, как можно замораживать наши объекты.
В следующей лекции мы рассмотрим объекты Date и объект Mass.