обработка данных

Нажмите сюда, если долго загружается,
либо "ESC" - отмена
 
Заказ обратного звонка
Заказать звонок
Наш специалист свяжется с Вами и ответит на все вопросы
Обработка данных
Наш специалист свяжется с Вами и ответит на все вопросы.
OK

Декораторы в JavaScript

Источник: https://habrahabr.ru
Время чтения: ~9 мин
Декораторы в JavaScript
Статьи
847
Источник: habrahabr.ru
Учитывая введение стандарта ES2015+, и то, что транспиляция в наше время — обычное дело, многие программисты сталкиваются с новыми возможностями JavaScript в реальном коде и в учебных материалах. Одна из таких возможностей — декораторы. Во всех этих новшествах немудрено и запутаться, поэтому сегодня поговорим о том, что такое декораторы, и о том, как их использовать для того, чтобы сделать код чище и понятнее.

Декораторы обрели популярность благодаря их применению в Angular 2+. В Angular этот функционал реализуется средствами TypeScript. Сейчас предложение по введению декораторов в JavaScript находится в состоянии Stage 2 Draft. Это означает, что работа над ними, в основном, завершена, но они всё ещё могут подвергаться изменениям. Декораторы должны стать частью следующего обновления языка.
Что такое декоратор?
В простейшем виде декоратор — это способ оборачивания одного фрагмента кода в другой. Буквально — «декорирование» фрагмента кода.

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

Подобное вполне реализуемо стандартными средствами JavaScript. Выглядит это как вызов некоей функции, которая оборачивает другую:
CODE
В этом примере показано, как создаётся новая функция, которая назначается константе wrapped. Эта функция может быть вызвана точно так же, как и функция doSomething, и делать она будет то же самое. Разница заключается в том, что до и после вызова оборачиваемой функции будет выполнено логирование. Вот что произойдёт, если поэкспериментировать с функциями doSomething и wrapped.
CODE
Как применять декораторы в JavaScript?
Декораторы в JavaScript используют специальный синтаксис, они имеют префикс в виде символа @, их размещают непосредственно перед кодом, который хотят декорировать.

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

Например:
CODE
Здесь показано объявление класса и применение трёх декораторов. Два из них относятся к самому классу, и один — к свойству класса. Вот каковы роли этих декораторов:

- @log может логировать все обращения к классу.
- @immutable способен сделать класс иммутабельным — возможно, он вызовет Object.freeze для новых экземпляров класса.
- @time записывает сведения о длительности исполнения методов и выводит эти сведения в лог с уникальным тегом.

Сегодня использование декораторов требует применение транспилятора, так как их пока не поддерживают ни браузеры, ни Node.js.

Если вы используете Babel, для работы с декораторами можно обратиться к плагину transform-decorators-legacy.

Обратите внимание на то, что в названии этого плагина используется слово «legacy», которое можно трактовать как указание на некую устаревшую технологию. Дело тут в том, что здесь поддерживается то, как декораторы обрабатывает Babel 5. Этот подход может отличаться от той формы, которая, в итоге, будет стандартизирована.
Зачем нужны декораторы?
Функциональная композиция в JavaScript без особых проблем реализуется и стандартными средствами. Однако, тот же подход либо очень сложно, либо невозможно применить к другим программным конструкциям, например — к классам и их свойствам. Предложенные нововведения позволяют использовать декораторы и с классами, и с их свойствами. Вероятно, в будущих версиях JavaScript можно ожидать дальнейшего развития декораторов.

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

Декораторы выполняются один раз при запуске программы, декорируемый код заменяется возвращаемым значением.
Декораторы членов класса
Декораторы членов класса применяются к свойствам, методам, геттерам и сеттерам.

Эти функции-декораторы вызываются с тремя параметрами:

- target — класс, в котором находится декорируемый член класса.
- name — имя члена класса.
- descriptor — дескриптор члена класса. Это, по существу, объект, который был бы передан методу Object.defineProperty.

Вот классический пример, который демонстрирует использование декоратора @readonly. Этот декоратор реализован так:
CODE
Декоратор устанавливает флаг writable дескриптора свойства в значение false.

Затем этот декоратор используется с членами класса следующим образом:
CODE
Как видно по сообщению об ошибке, декоратор сработал так, как ожидается. Мы можем пойти и дальше, например, изменив поведение декорируемой функции, фактически, заменив её новой функцией. Давайте, с помощью декоратора, выведем в лог аргументы функции, и то, что она возвращает:
CODE
Эта конструкция заменяет метод новым, который логирует аргументы, вызывает исходный метод, а затем логирует то, что он возвращает.

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

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

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

- Имеется функция log, которая принимает единственный аргумент — name.
- Эта функция возвращает ещё одну функцию, которая и является декоратором.

Возвращённая функция идентична декоратору log, который мы описывали выше, за исключением того, что она использует параметр name из внешней функции.

Пользоваться всем этим можно так:
CODE
Сразу видно, что такой подход позволяет различать строки логов благодаря назначенному тегу.

Здесь выполняется вызов функции вида log('some tag'), а затем то, что было возвращено из этого вызова, используется как декоратор для метода sum.
Декораторы классов
Декораторы классов применяются ко всему определению класса. Функция-декоратор вызывается с единственным параметром, которым является декорируемая функция-конструктор класса.

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

В целом, декораторы классов менее полезны, чем декораторы членов классов, так как всё, что тут можно сделать, сводится к замене конструктора класса.

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

Например:
CODE
Как видно, при выполнении конструктора класса Example будет выполнено логирование аргументов конструктора, которые используются при создании экземпляра этого класса. Это — именно то, чего мы добивались.

Для передачи параметров декораторам классов можно воспользоваться уже описанным подходом:
CODE
Декораторы в реальных проектах
Вот несколько примеров использования декораторов в популярных библиотеках.
Библиотека Core Decorators
Есть отличная библиотека Core Decorators, которая предоставляет готовые к использованию декораторы общего назначения. Среди поддерживаемой ими функциональности — тайминг вызовов методов, уведомления об устаревших конструкциях, проверка того, является ли некий объект неизменяемым.
Библиотека React
В React найдено хорошее применение концепции компонентов высшего порядка. Это — компоненты React, написанные как функции и служащие обёртками для других компонентов.

Они — идеальные кандидаты на использование в качестве декораторов, так как для того, чтобы использовать их в таком качестве, потребуются минимальные усилия. Например, в библиотеке Redux есть функция connect, которая используется для подключения компонентов React. Без декораторов работа с этой функцией может выглядеть так:
CODE
Если же переписать это с использованием декораторов, получится следующее:
CODE
Функционал получился тот же, но выглядит всё это гораздо симпатичнее.
Библиотека MobX
Декораторы широко используются в библиотеке MobX. Например, для обеспечения нужного поведения системы надо просто добавлять к полям декораторы Observable или Computed, а с классами использовать декоратор Observers.
Итоги
Мы поговорили о том, как создавать и использовать декораторы в JavaScript, в частности — рассмотрели особенности работы с декораторами членов класса. Такой подход позволяет писать вспомогательный код, представленный функциями-декораторами, который можно применять для изменения поведения методов различных классов. Синтаксис декораторов позволяет упростить тексты программ, сделать их чище и понятнее. С декораторами в JavaScript можно работать уже сегодня, они нашли применение в популярных библиотеках. Однако, полагаем, после того, как их напрямую будут поддерживать браузеры и Node.js, у них найдётся множество новых поклонников.
Поделиться
Поделиться
Поделиться
Поделиться
Поделиться
Поделиться
Поделиться
Подписка на новости. Получайте важное первым
ПОДПИСАТЬСЯ