Декораторы в TypeScript предоставляют мощный механизм для изменения поведения классов, методов, свойств и параметров. Хотя декораторы могут показаться современным удобством, они основаны на хорошо зарекомендовавшем себя шаблоне декораторов, используемом в объектно-ориентированном программировании. Абстрагируя общие функции, такие как ведение журнала, проверка или контроль доступа, декораторы позволяют разработчикам писать более чистый и удобный в обслуживании код.
В этой статье мы рассмотрим декораторы с самых первых принципов, разберем их основные функции и реализуем их с нуля. Попутно мы рассмотрим некоторые реальные приложения, демонстрирующие полезность декораторов в повседневной разработке TypeScript.
В TypeScript декоратор — это просто функция, которую можно прикрепить к классу, методу, свойству или параметру. Эта функция выполняется во время разработки, что дает вам возможность изменить поведение или структуру кода до его запуска. Декораторы обеспечивают метапрограммирование, позволяя нам добавлять дополнительные функции без изменения исходной логики.
Давайте начнем с простого примера декоратора метода, который регистрирует вызов метода:
function log(target: any, propertyKey: string, descriptor: PropertyDescriptor) { const originalMethod = descriptor.value; descriptor.value = function (...args: any[]) { console.log(`Calling ${propertyKey} with arguments: ${args}`); return originalMethod.apply(this, args); }; return descriptor; } class Example { @log greet(name: string) { return `Hello, ${name}`; } } const example = new Example(); example.greet('John');
Здесь декоратор журнала оборачивает методприветствия, записывая его вызов и параметры перед его выполнением. Этот шаблон полезен для отделения сквозных задач, таких как ведение журнала, от основной логики.
Декораторы в TypeScript — это функции, которые принимают метаданные, относящиеся к элементу, который они украшают. На основе этих метаданных (например, прототипов классов, имен методов или дескрипторов свойств) декораторы могут изменять поведение или даже заменять декорированный объект.
Декораторы можно применять к различным целям, каждая из которых имеет разные цели:
function classDecorator(constructor: Function) { // Modify or extend the class constructor or prototype }
function methodDecorator(target: any, propertyKey: string, descriptor: PropertyDescriptor) { // Modify the method's descriptor }
function propertyDecorator(target: any, propertyKey: string) { // Modify the behavior of the property }
function parameterDecorator(target: any, propertyKey: string, parameterIndex: number) { // Modify or inspect the method's parameter }
Одной из самых мощных особенностей декораторов является их способность принимать аргументы, что позволяет настраивать их поведение. Например, давайте создадим декоратор метода, который регистрирует вызовы метода условно на основе аргумента.
function logConditionally(shouldLog: boolean) { return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) { const originalMethod = descriptor.value; descriptor.value = function (...args: any[]) { if (shouldLog) { console.log(`Calling ${propertyKey} with arguments: ${args}`); } return originalMethod.apply(this, args); }; return descriptor; }; } class Example { @logConditionally(true) greet(name: string) { return `Hello, ${name}`; } } const example = new Example(); example.greet('TypeScript Developer');
Передавая true декоратору logConditionally, мы гарантируем, что метод протоколирует свое выполнение. Если мы передаем false, ведение журнала пропускается. Эта гибкость является ключом к тому, чтобы сделать декораторы универсальными и многоразовыми.
Декораторы нашли практическое применение во многих библиотеках и фреймворках. Вот несколько ярких примеров, иллюстрирующих, как декораторы оптимизируют сложную функциональность:
import { IsEmail, IsNotEmpty } from 'class-validator'; class User { @IsNotEmpty() name: string; @IsEmail() email: string; }
В этом примере декораторы @IsEmail и @IsNotEmpty гарантируют, что поле электронной почты является действительным адресом электронной почты, а поле имени не пусто. Эти декораторы экономят время, устраняя необходимость в логике ручной проверки.
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm'; @Entity() class User { @PrimaryGeneratedColumn() id: number; @Column() name: string; @Column() email: string; }
Здесь @Entity, @Column и @PrimaryGeneratedColumn определяют структуру таблицы User. Эти декораторы абстрагируют сложность создания таблиц SQL, делая код более читабельным и удобным в обслуживании.
@Injectable({ providedIn: 'root', }) class UserService { constructor(private http: HttpClient) {} }
В этом случае декоратор @Injectable сигнализирует системе внедрения зависимостей Angular, что UserService должен быть предоставлен глобально. Это обеспечивает плавную интеграцию сервисов в приложении.
Декораторы по своей сути — это просто функции. Давайте разберем процесс создания декораторов с нуля:
Декоратор класса получает конструктор класса и может использоваться для изменения прототипа класса или даже замены конструктора.
function AddTimestamp(constructor: Function) { constructor.prototype.timestamp = new Date(); } @AddTimestamp class MyClass { id: number; constructor(id: number) { this.id = id; } } const instance = new MyClass(1); console.log(instance.timestamp); // Outputs the current timestamp
Декоратор метода изменяет дескриптор метода, позволяя вам изменить поведение самого метода.
function logExecutionTime(target: any, propertyKey: string, descriptor: PropertyDescriptor) { const originalMethod = descriptor.value; descriptor.value = function (...args: any[]) { const start = performance.now(); const result = originalMethod.apply(this, args); const end = performance.now(); console.log(`${propertyKey} executed in ${end - start}ms`); return result; }; return descriptor; } class Service { @logExecutionTime execute() { // Simulate work for (let i = 0; iДекоратор недвижимости
Декоратор свойств позволяет перехватывать доступ к свойствам и их изменение, что может быть полезно для отслеживания изменений.
function trackChanges(target: any, propertyKey: string) { let value = target[propertyKey]; const getter = () => value; const setter = (newValue: any) => { console.log(`${propertyKey} changed from ${value} to ${newValue}`); value = newValue; }; Object.defineProperty(target, propertyKey, { get: getter, set: setter, }); } class Product { @trackChanges price: number; constructor(price: number) { this.price = price; } } const product = new Product(100); product.price = 200; // Logs the changeЗаключение
Декораторы в TypeScript позволяют абстрагировать и повторно использовать функциональность простым и декларативным способом. Независимо от того, работаете ли вы с проверкой, ORM или внедрением зависимостей, декораторы помогают сократить количество шаблонов и сохранить модульность и удобство обслуживания вашего кода. Понимание основных принципов их работы облегчает использование всего их потенциала и разработку индивидуальных решений, адаптированных к вашему приложению.
Более подробно изучив структуру и реальное применение декораторов, вы теперь увидели, как они могут упростить сложные задачи кодирования и оптимизировать код в различных областях.
Отказ от ответственности: Все предоставленные ресурсы частично взяты из Интернета. В случае нарушения ваших авторских прав или других прав и интересов, пожалуйста, объясните подробные причины и предоставьте доказательства авторских прав или прав и интересов, а затем отправьте их по электронной почте: [email protected]. Мы сделаем это за вас как можно скорее.
Copyright© 2022 湘ICP备2022001581号-3