JavaScript 装饰器模式详解
装饰器(Decorator)是一种强大的设计模式,允许我们在不修改原有代码的情况下,为对象添加新的功能。随着ES2024标准的推进,JavaScript原生装饰器即将到来,本文将全面解析装饰器的原理与实践。
装饰器模式基础
核心概念
装饰器模式的核心思想是组合优于继承,通过包装对象来扩展功能,而不是通过继承来实现。
javascript
// 传统继承方式的问题
class BasicCoffee {
cost() {
return 10;
}
description() {
return "Basic Coffee";
}
}
class MilkCoffee extends BasicCoffee {
cost() {
return super.cost() + 2;
}
description() {
return super.description() + ", Milk";
}
}
class SugarMilkCoffee extends MilkCoffee {
cost() {
return super.cost() + 1;
}
description() {
return super.description() + ", Sugar";
}
}
// 问题:需要为每种组合创建新类,类爆炸问题
装饰器模式解决方案
javascript
// 装饰器模式实现
class Coffee {
constructor() {
this.cost = 10;
this.description = "Basic Coffee";
}
}
// 装饰器基类
class CoffeeDecorator {
constructor(coffee) {
this.coffee = coffee;
}
getCost() {
return this.coffee.cost;
}
getDescription() {
return this.coffee.description;
}
}
// 具体装饰器
class MilkDecorator extends CoffeeDecorator {
constructor(coffee) {
super(coffee);
}
getCost() {
return this.coffee.getCost() + 2;
}
getDescription() {
return this.coffee.getDescription() + ", Milk";
}
}
class SugarDecorator extends CoffeeDecorator {
constructor(coffee) {
super(coffee);
}
getCost() {
return this.coffee.getCost() + 1;
}
getDescription() {
return this.coffee.getDescription() + ", Sugar";
}
}
// 使用装饰器
let coffee = new Coffee();
coffee = new MilkDecorator(coffee);
coffee = new SugarDecorator(coffee);
console.log(coffee.getDescription()); // "Basic Coffee, Milk, Sugar"
console.log(coffee.getCost()); // 13
JavaScript装饰器语法
当前状态(Stage 3提案)
javascript
// 类装饰器
@logged
@sealed
class User {
@readonly
@validate
name = '';
@debounce(300)
@authorize('admin')
async updateProfile(data) {
// 更新用户资料
}
@memoize
get fullName() {
return `${this.firstName} ${this.lastName}`;
}
}
// 装饰器实现
function logged(target, context) {
if (context.kind === 'class') {
return class extends target {
constructor(...args) {
console.log(`Creating instance of ${target.name}`);
super(...args);
}
};
}
}
function readonly(target, context) {
if (context.kind === 'field') {
return function(initialValue) {
return {
get() {
return initialValue;
},
set() {
throw new Error('Cannot modify readonly property');
}
};
};
}
}
TypeScript装饰器(实验性)
typescript
// 启用装饰器支持
// tsconfig.json
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}
// 类装饰器
function Component(config: ComponentConfig) {
return function<T extends new(...args: any[]) => {}>(constructor: T) {
return class extends constructor {
componentConfig = config;
render() {
console.log(`Rendering ${config.name}`);
return super.render?.() || null;
}
};
};
}
// 方法装饰器
function Log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function(...args: any[]) {
console.log(`Calling ${propertyKey} with args:`, args);
const result = originalMethod.apply(this, args);
console.log(`${propertyKey} returned:`, result);
return result;
};
return descriptor;
}
// 属性装饰器
function MinLength(length: number) {
return function(target: any, propertyKey: string) {
let value: string;
const getter = () => value;
const setter = (newValue: string) => {
if (newValue.length < length) {
throw new Error(`${propertyKey} must be at least ${length} characters`);
}
value = newValue;
};
Object.defineProperty(target, propertyKey, {
get: getter,
set: setter,
enumerable: true,
configurable: true
});
};
}
// 参数装饰器
function Required(target: any, propertyKey: string, parameterIndex: number) {
const existingRequiredParameters: number[] =
Reflect.getOwnMetadata('required', target, propertyKey) || [];
existingRequiredParameters.push(parameterIndex);
Reflect.defineMetadata('required', existingRequiredParameters, target, propertyKey);
}
// 使用示例
@Component({ name: 'UserCard', version: '1.0' })
class UserCard {
@MinLength(3)
username: string = '';
@Log
updateUser(@Required name: string, age?: number) {
this.username = name;
return { name, age };
}
}
实用装饰器实现
1. 性能监控装饰器
javascript
// 性能监控装饰器
function performance(target, context) {
if (context.kind === 'method') {
return function(...args) {
const start = performance.now();
const result = target.apply(this, args);
if (result instanceof Promise) {
return result.finally(() => {
const end = performance.now();
console.log(`${context.name} took ${end - start}ms`);
});
} else {
const end = performance.now();
console.log(`${context.name} took ${end - start}ms`);
return result;
}
};
}
}
// 使用示例
class DataService {
@performance
async fetchUsers() {
const response = await fetch('/api/users');
return response.json();
}
@performance
processData(data) {
return data.map(item => ({
...item,
processed: true
}));
}
}
2. 缓存装饰器
javascript
// 缓存装饰器
function memoize(ttl = 60000) { // 默认缓存1分钟
const cache = new Map();
return function(target, context) {
if (context.kind === 'method') {
return function(...args) {
const key = JSON.stringify(args);
const cached = cache.get(key);
if (cached && Date.now() - cached.timestamp < ttl) {
console.log('Cache hit for', context.name);
return cached.value;
}
const result = target.apply(this, args);
if (result instanceof Promise) {
return result.then(value => {
cache.set(key, { value, timestamp: Date.now() });
return value;
});
} else {
cache.set(key, { value: result, timestamp: Date.now() });
return result;
}
};
}
};
}
// 使用示例
class ApiService {
@memoize(30000) // 缓存30秒
async getUser(id) {
console.log('Fetching user from API');
const response = await fetch(`/api/users/${id}`);
return response.json();
}
@memoize(60000) // 缓存1分钟
calculateExpensiveOperation(data) {
console.log('Performing expensive calculation');
return data.reduce((sum, item) => sum + item.value, 0);
}
}
3. 防抖和节流装饰器
javascript
// 防抖装饰器
function debounce(delay) {
let timeoutId;
return function(target, context) {
if (context.kind === 'method') {
return function(...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
target.apply(this, args);
}, delay);
};
}
};
}
// 节流装饰器
function throttle(delay) {
let lastCall = 0;
return function(target, context) {
if (context.kind === 'method') {
return function(...args) {
const now = Date.now();
if (now - lastCall >= delay) {
lastCall = now;
return target.apply(this, args);
}
};
}
};
}
// 使用示例
class SearchComponent {
@debounce(300)
onSearchInput(query) {
console.log('Searching for:', query);
this.performSearch(query);
}
@throttle(1000)
onScroll(event) {
console.log('Scroll event handled');
this.updateScrollPosition(event.target.scrollTop);
}
}
4. 权限验证装饰器
javascript
// 权限验证装饰器
function authorize(requiredRole) {
return function(target, context) {
if (context.kind === 'method') {
return function(...args) {
const currentUser = this.getCurrentUser?.() || window.currentUser;
if (!currentUser) {
throw new Error('User not authenticated');
}
if (!currentUser.roles.includes(requiredRole)) {
throw new Error(`Access denied. Required role: ${requiredRole}`);
}
return target.apply(this, args);
};
}
};
}
// 审计日志装饰器
function audit(action) {
return function(target, context) {
if (context.kind === 'method') {
return function(...args) {
const user = this.getCurrentUser?.() || window.currentUser;
const timestamp = new Date().toISOString();
console.log(`Audit: ${user?.id} performed ${action} at ${timestamp}`);
// 发送审计日志到服务器
this.sendAuditLog?.({
userId: user?.id,
action,
timestamp,
args: args.map(arg => typeof arg === 'object' ? JSON.stringify(arg) : arg)
});
return target.apply(this, args);
};
}
};
}
// 使用示例
class AdminService {
@authorize('admin')
@audit('DELETE_USER')
async deleteUser(userId) {
console.log(`Deleting user ${userId}`);
await fetch(`/api/users/${userId}`, { method: 'DELETE' });
}
@authorize('moderator')
@audit('UPDATE_USER_ROLE')
async updateUserRole(userId, newRole) {
console.log(`Updating user ${userId} role to ${newRole}`);
await fetch(`/api/users/${userId}/role`, {
method: 'PUT',
body: JSON.stringify({ role: newRole })
});
}
}
5. 重试装饰器
javascript
// 重试装饰器
function retry(maxAttempts = 3, delay = 1000) {
return function(target, context) {
if (context.kind === 'method') {
return async function(...args) {
let lastError;
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
return await target.apply(this, args);
} catch (error) {
lastError = error;
console.log(`Attempt ${attempt} failed:`, error.message);
if (attempt < maxAttempts) {
await new Promise(resolve => setTimeout(resolve, delay * attempt));
}
}
}
throw new Error(`Failed after ${maxAttempts} attempts: ${lastError.message}`);
};
}
};
}
// 断路器装饰器
function circuitBreaker(failureThreshold = 5, timeout = 60000) {
let failures = 0;
let lastFailureTime = 0;
let state = 'CLOSED'; // CLOSED, OPEN, HALF_OPEN
return function(target, context) {
if (context.kind === 'method') {
return async function(...args) {
const now = Date.now();
// 检查断路器状态
if (state === 'OPEN') {
if (now - lastFailureTime > timeout) {
state = 'HALF_OPEN';
} else {
throw new Error('Circuit breaker is OPEN');
}
}
try {
const result = await target.apply(this, args);
// 成功时重置计数器
if (state === 'HALF_OPEN') {
state = 'CLOSED';
failures = 0;
}
return result;
} catch (error) {
failures++;
lastFailureTime = now;
if (failures >= failureThreshold) {
state = 'OPEN';
}
throw error;
}
};
}
};
}
// 使用示例
class ExternalApiService {
@retry(3, 2000)
@circuitBreaker(5, 30000)
async fetchExternalData(endpoint) {
const response = await fetch(`https://external-api.com${endpoint}`);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return response.json();
}
}
高级装饰器模式
1. 装饰器工厂
javascript
// 装饰器工厂 - 创建可配置的装饰器
function createValidator(validationRules) {
return function validate(target, context) {
if (context.kind === 'method') {
return function(...args) {
// 验证参数
validationRules.forEach((rule, index) => {
const value = args[index];
if (!rule.validator(value)) {
throw new Error(`Parameter ${index}: ${rule.message}`);
}
});
return target.apply(this, args);
};
}
};
}
// 创建具体的验证装饰器
const validateUserInput = createValidator([
{
validator: (value) => typeof value === 'string' && value.length > 0,
message: 'Name must be a non-empty string'
},
{
validator: (value) => typeof value === 'number' && value >= 0,
message: 'Age must be a non-negative number'
}
]);
class UserService {
@validateUserInput
createUser(name, age) {
return { name, age, id: Date.now() };
}
}
2. 组合装饰器
javascript
// 装饰器组合工具
function compose(...decorators) {
return function(target, context) {
return decorators.reduceRight((acc, decorator) => {
return decorator(acc, context) || acc;
}, target);
};
}
// 创建组合装饰器
const apiMethod = compose(
performance,
retry(3, 1000),
memoize(30000),
authorize('user')
);
class ApiController {
@apiMethod
async getUserData(userId) {
const response = await fetch(`/api/users/${userId}`);
return response.json();
}
}
3. 元数据装饰器
javascript
// 元数据装饰器
function metadata(key, value) {
return function(target, context) {
if (!target.metadata) {
target.metadata = {};
}
target.metadata[key] = value;
return target;
};
}
// 路由装饰器
function route(path, method = 'GET') {
return function(target, context) {
return metadata('route', { path, method })(target, context);
};
}
// 中间件装饰器
function middleware(...middlewares) {
return function(target, context) {
return metadata('middlewares', middlewares)(target, context);
};
}
// 使用示例
class UserController {
@route('/users/:id', 'GET')
@middleware('auth', 'rateLimit')
async getUser(req, res) {
const user = await this.userService.findById(req.params.id);
res.json(user);
}
@route('/users', 'POST')
@middleware('auth', 'validation')
async createUser(req, res) {
const user = await this.userService.create(req.body);
res.json(user);
}
}
// 路由注册器
function registerRoutes(controller) {
const prototype = Object.getPrototypeOf(controller);
const methods = Object.getOwnPropertyNames(prototype);
methods.forEach(methodName => {
const method = prototype[methodName];
if (method.metadata?.route) {
const { path, method: httpMethod } = method.metadata.route;
const middlewares = method.metadata.middlewares || [];
console.log(`Registering route: ${httpMethod} ${path}`);
// 实际的路由注册逻辑
}
});
}
实际应用场景
1. React组件装饰器
javascript
// React组件装饰器
function withLoading(WrappedComponent) {
return function LoadingWrapper(props) {
const [loading, setLoading] = React.useState(false);
const withLoadingProps = {
...props,
setLoading,
loading
};
if (loading) {
return <div className="loading">Loading...</div>;
}
return <WrappedComponent {...withLoadingProps} />;
};
}
function withErrorBoundary(WrappedComponent) {
return class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
console.error('Error caught by boundary:', error, errorInfo);
}
render() {
if (this.state.hasError) {
return <div className="error">Something went wrong.</div>;
}
return <WrappedComponent {...this.props} />;
}
};
}
// 使用装饰器
@withErrorBoundary
@withLoading
class UserProfile extends React.Component {
async componentDidMount() {
this.props.setLoading(true);
try {
const user = await fetchUser(this.props.userId);
this.setState({ user });
} finally {
this.props.setLoading(false);
}
}
render() {
return (
<div className="user-profile">
<h1>{this.state.user?.name}</h1>
<p>{this.state.user?.email}</p>
</div>
);
}
}
2. Vue组件装饰器
javascript
// Vue组件装饰器
function VueComponent(options) {
return function(target) {
return {
...options,
data() {
return new target().data || {};
},
methods: Object.getOwnPropertyNames(target.prototype)
.filter(name => name !== 'constructor')
.reduce((methods, name) => {
methods[name] = target.prototype[name];
return methods;
}, {})
};
};
}
function Prop(options = {}) {
return function(target, propertyKey) {
if (!target.constructor.props) {
target.constructor.props = {};
}
target.constructor.props[propertyKey] = options;
};
}
// 使用示例
@VueComponent({
template: `
<div class="user-card">
<h3>{{ name }}</h3>
<p>{{ email }}</p>
<button @click="updateUser">Update</button>
</div>
`
})
class UserCard {
@Prop({ type: String, required: true })
name;
@Prop({ type: String, required: true })
email;
data = {
loading: false
};
@debounce(300)
updateUser() {
this.loading = true;
// 更新用户逻辑
}
}
3. 数据模型装饰器
javascript
// 数据模型装饰器
function Model(tableName) {
return function(target) {
target.tableName = tableName;
target.fields = {};
return target;
};
}
function Field(options = {}) {
return function(target, propertyKey) {
if (!target.constructor.fields) {
target.constructor.fields = {};
}
target.constructor.fields[propertyKey] = options;
};
}
function Validate(validator) {
return function(target, propertyKey) {
const originalSetter = Object.getOwnPropertyDescriptor(target, propertyKey)?.set;
Object.defineProperty(target, propertyKey, {
set(value) {
if (!validator(value)) {
throw new Error(`Invalid value for ${propertyKey}`);
}
if (originalSetter) {
originalSetter.call(this, value);
} else {
this[`_${propertyKey}`] = value;
}
},
get() {
return this[`_${propertyKey}`];
}
});
};
}
// 使用示例
@Model('users')
class User {
@Field({ type: 'string', primaryKey: true })
id;
@Field({ type: 'string', required: true })
@Validate(value => typeof value === 'string' && value.length > 0)
name;
@Field({ type: 'string', unique: true })
@Validate(value => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value))
email;
@Field({ type: 'number', min: 0 })
@Validate(value => typeof value === 'number' && value >= 0)
age;
}
性能考虑
1. 装饰器性能优化
javascript
// 性能优化的装饰器实现
function optimizedMemoize(ttl = 60000) {
// 使用WeakMap避免内存泄漏
const cache = new WeakMap();
return function(target, context) {
if (context.kind === 'method') {
return function(...args) {
// 为每个实例创建独立的缓存
if (!cache.has(this)) {
cache.set(this, new Map());
}
const instanceCache = cache.get(this);
const key = JSON.stringify(args);
const cached = instanceCache.get(key);
if (cached && Date.now() - cached.timestamp < ttl) {
return cached.value;
}
const result = target.apply(this, args);
instanceCache.set(key, { value: result, timestamp: Date.now() });
// 定期清理过期缓存
if (instanceCache.size > 100) {
this.cleanupCache(instanceCache, ttl);
}
return result;
};
}
};
}
// 缓存清理方法
function cleanupCache(cache, ttl) {
const now = Date.now();
for (const [key, value] of cache.entries()) {
if (now - value.timestamp > ttl) {
cache.delete(key);
}
}
}
2. 装饰器链优化
javascript
// 优化装饰器链的执行顺序
function createOptimizedDecorator(decorators) {
// 分析装饰器依赖关系
const sortedDecorators = sortDecoratorsByPriority(decorators);
return function(target, context) {
return sortedDecorators.reduce((acc, decorator) => {
return decorator(acc, context) || acc;
}, target);
};
}
function sortDecoratorsByPriority(decorators) {
return decorators.sort((a, b) => {
const priorityA = a.priority || 0;
const priorityB = b.priority || 0;
return priorityB - priorityA; // 高优先级先执行
});
}
// 为装饰器添加优先级
performance.priority = 10; // 性能监控优先级最高
authorize.priority = 9; // 权限验证次之
memoize.priority = 5; // 缓存优先级中等
debounce.priority = 1; // 防抖优先级最低
最佳实践
1. 装饰器设计原则
javascript
// 1. 单一职责原则
// ❌ 错误:一个装饰器做太多事情
function badDecorator(target, context) {
// 同时处理缓存、日志、权限验证
return function(...args) {
// 权限检查
if (!this.hasPermission()) throw new Error('No permission');
// 缓存检查
const cached = this.getCache(args);
if (cached) return cached;
// 执行方法
const result = target.apply(this, args);
// 记录日志
console.log('Method executed');
// 缓存结果
this.setCache(args, result);
return result;
};
}
// ✅ 正确:每个装饰器只负责一件事
const secureApiMethod = compose(
authorize('user'), // 只负责权限验证
memoize(30000), // 只负责缓存
performance, // 只负责性能监控
audit('API_CALL') // 只负责审计日志
);
2. 装饰器可组合性
javascript
// 2. 确保装饰器可以安全组合
function safeDecorator(target, context) {
// 保存原始方法的元数据
const originalMetadata = target.metadata || {};
const decoratedMethod = function(...args) {
// 装饰器逻辑
return target.apply(this, args);
};
// 保持元数据
decoratedMethod.metadata = {
...originalMetadata,
decoratedBy: [...(originalMetadata.decoratedBy || []), 'safeDecorator']
};
return decoratedMethod;
}
3. 错误处理
javascript
// 3. 装饰器中的错误处理
function robustDecorator(target, context) {
return function(...args) {
try {
return target.apply(this, args);
} catch (error) {
// 记录错误但不阻止执行
console.error(`Error in ${context.name}:`, error);
// 可以选择重新抛出或返回默认值
if (error instanceof CriticalError) {
throw error;
}
return null; // 或其他默认值
}
};
}
4. 类型安全(TypeScript)
typescript
// 4. 类型安全的装饰器
function typedDecorator<T extends (...args: any[]) => any>(
target: T,
context: ClassMethodDecoratorContext
): T {
return ((...args: Parameters<T>): ReturnType<T> => {
// 装饰器逻辑
return target.apply(this, args);
}) as T;
}
// 泛型装饰器工厂
function createTypedValidator<T extends any[]>(
validators: { [K in keyof T]: (value: T[K]) => boolean }
) {
return function<F extends (...args: T) => any>(
target: F,
context: ClassMethodDecoratorContext
): F {
return ((...args: T): ReturnType<F> => {
validators.forEach((validator, index) => {
if (!validator(args[index])) {
throw new Error(`Invalid argument at position ${index}`);
}
});
return target.apply(this, args);
}) as F;
};
}
调试和测试
1. 装饰器调试技巧
javascript
// 调试装饰器
function debug(target, context) {
const originalMethod = target;
return function(...args) {
console.group(`🔍 Debug: ${context.name}`);
console.log('Arguments:', args);
console.log('Context:', this);
const start = performance.now();
let result;
let error;
try {
result = originalMethod.apply(this, args);
if (result instanceof Promise) {
return result
.then(value => {
console.log('Async Result:', value);
console.log(`⏱️ Duration: ${performance.now() - start}ms`);
console.groupEnd();
return value;
})
.catch(err => {
console.error('Async Error:', err);
console.log(`⏱️ Duration: ${performance.now() - start}ms`);
console.groupEnd();
throw err;
});
} else {
console.log('Result:', result);
console.log(`⏱️ Duration: ${performance.now() - start}ms`);
console.groupEnd();
return result;
}
} catch (err) {
console.error('Error:', err);
console.log(`⏱️ Duration: ${performance.now() - start}ms`);
console.groupEnd();
throw err;
}
};
}
// 使用调试装饰器
class DebugService {
@debug
async fetchData(id) {
const response = await fetch(`/api/data/${id}`);
return response.json();
}
}
2. 装饰器单元测试
javascript
// 装饰器测试工具
function createTestableDecorator(decorator) {
return {
decorator,
test: (target, context, ...args) => {
const decoratedMethod = decorator(target, context);
return decoratedMethod.apply({}, args);
}
};
}
// 测试示例
describe('Memoize Decorator', () => {
let callCount = 0;
const testMethod = function(x, y) {
callCount++;
return x + y;
};
const testableDecorator = createTestableDecorator(memoize(1000));
beforeEach(() => {
callCount = 0;
});
it('should cache results', () => {
const result1 = testableDecorator.test(testMethod, { name: 'add' }, 1, 2);
const result2 = testableDecorator.test(testMethod, { name: 'add' }, 1, 2);
expect(result1).toBe(3);
expect(result2).toBe(3);
expect(callCount).toBe(1); // 只调用一次,第二次使用缓存
});
it('should not cache different arguments', () => {
testableDecorator.test(testMethod, { name: 'add' }, 1, 2);
testableDecorator.test(testMethod, { name: 'add' }, 2, 3);
expect(callCount).toBe(2); // 不同参数,调用两次
});
});
装饰器生态系统
1. 流行的装饰器库
javascript
// reflect-metadata - 元数据支持
import 'reflect-metadata';
function Injectable(target) {
Reflect.defineMetadata('injectable', true, target);
return target;
}
function Inject(token) {
return function(target, propertyKey, parameterIndex) {
const existingTokens = Reflect.getOwnMetadata('inject-tokens', target) || [];
existingTokens[parameterIndex] = token;
Reflect.defineMetadata('inject-tokens', existingTokens, target);
};
}
// 依赖注入示例
@Injectable
class UserService {
constructor(
@Inject('HttpClient') private http,
@Inject('Logger') private logger
) {}
async getUser(id) {
this.logger.info(`Fetching user ${id}`);
return this.http.get(`/users/${id}`);
}
}
2. 框架集成
javascript
// Express.js 装饰器
function Controller(basePath = '') {
return function(target) {
target.basePath = basePath;
return target;
};
}
function Get(path = '') {
return function(target, propertyKey, descriptor) {
if (!target.constructor.routes) {
target.constructor.routes = [];
}
target.constructor.routes.push({
method: 'GET',
path,
handler: propertyKey
});
return descriptor;
};
}
function Post(path = '') {
return function(target, propertyKey, descriptor) {
if (!target.constructor.routes) {
target.constructor.routes = [];
}
target.constructor.routes.push({
method: 'POST',
path,
handler: propertyKey
});
return descriptor;
};
}
// 使用示例
@Controller('/api/users')
class UserController {
@Get('/')
async getAllUsers(req, res) {
const users = await this.userService.findAll();
res.json(users);
}
@Get('/:id')
async getUser(req, res) {
const user = await this.userService.findById(req.params.id);
res.json(user);
}
@Post('/')
async createUser(req, res) {
const user = await this.userService.create(req.body);
res.json(user);
}
}
// 路由自动注册
function registerController(app, ControllerClass) {
const controller = new ControllerClass();
const basePath = ControllerClass.basePath || '';
const routes = ControllerClass.routes || [];
routes.forEach(route => {
const fullPath = basePath + route.path;
const method = route.method.toLowerCase();
const handler = controller[route.handler].bind(controller);
app[method](fullPath, handler);
console.log(`Registered route: ${route.method} ${fullPath}`);
});
}
未来发展
1. 标准化进程
javascript
// ES2024+ 装饰器新特性
class MyClass {
// 自动访问器装饰器
@reactive
accessor count = 0;
// 装饰器元数据
@metadata({ version: '1.0', author: 'developer' })
method() {
return 'Hello World';
}
}
// 装饰器元数据访问
function getMethodMetadata(target, methodName) {
return target[Symbol.metadata]?.[methodName];
}
2. 工具链支持
javascript
// Babel 插件配置
// babel.config.js
module.exports = {
plugins: [
['@babel/plugin-proposal-decorators', {
version: '2023-05'
}]
]
};
// TypeScript 配置
// tsconfig.json
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"useDefineForClassFields": false
}
}
总结
装饰器的优势
- 代码复用 - 横切关注点的优雅解决方案
- 关注点分离 - 业务逻辑与基础设施代码分离
- 可组合性 - 多个装饰器可以灵活组合
- 声明式编程 - 更直观的代码表达方式
使用建议
- 适度使用 - 避免过度装饰导致代码难以理解
- 文档完善 - 为自定义装饰器提供清晰的文档
- 测试覆盖 - 确保装饰器逻辑有充分的测试
- 性能考虑 - 注意装饰器对性能的影响
最佳实践总结
javascript
// 装饰器最佳实践检查清单
const decoratorBestPractices = {
design: [
'单一职责原则',
'可组合性设计',
'错误处理机制',
'类型安全保证'
],
implementation: [
'性能优化考虑',
'内存泄漏防护',
'元数据保持',
'调试友好性'
],
testing: [
'单元测试覆盖',
'集成测试验证',
'边界条件测试',
'性能基准测试'
],
documentation: [
'使用示例提供',
'参数说明清晰',
'副作用说明',
'兼容性说明'
]
};
装饰器模式为JavaScript开发带来了强大的元编程能力,合理使用可以显著提升代码的可维护性和可读性。随着标准化进程的推进,装饰器将成为现代JavaScript开发的重要工具。
装饰器不仅仅是语法糖,它代表了一种编程思维的转变——从命令式到声明式,从继承到组合。掌握装饰器模式,将为你的JavaScript开发打开新的可能性。