我是兰瓶Coding,一枚刚踏入鸿蒙领域的转型小白,原是移动开发中级,如下是我学习笔记《零基础学鸿蒙》,若对你所有帮助,还请不吝啬的给个大大的赞~

前言

有时候我真的觉得,装饰器(Decorator) 这玩意儿,就像编程界的“调味料”——不放,代码也能跑;但放了,瞬间风味升级!🍜
尤其在 ArkTS(HarmonyOS的高级TypeScript方言) 里,装饰器不仅能“美化”代码,还能“强化”它的灵魂。
今天,我们就来深挖:在ArkTS中,怎么优雅地实现一个自定义装饰器?
别怕,我保证你看完之后,不仅懂,还能自己写出花!🌸

🧩 一、装饰器的原理 —— 编译器的“偷窥”艺术

说到装饰器,我们得先揭开它的神秘面纱。
其实,它就是一种语法糖,允许我们在编译阶段拦截类、方法或属性的定义,然后“顺手”做点什么。

ArkTS在编译时,会触发一个编译器钩子(Compiler Hook),它能捕获到被装饰的目标对象(类、方法、属性),并将我们定义的逻辑注入进去。
这就像我们偷偷在代码背后放了一个监听器——每当编译器遇到@Something时,就会回头问我们:“嘿,这玩意儿要怎么改?”

来点代码更直观👇

// demo1: 基础装饰器例子
function Log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
  const originalMethod = descriptor.value;
  descriptor.value = function (...args: any[]) {
    console.log(`[Log] 调用方法: ${propertyKey}, 参数:`, args);
    const result = originalMethod.apply(this, args);
    console.log(`[Log] 返回结果:`, result);
    return result;
  };
  return descriptor;
}

class DemoService {
  @Log
  fetchData(url: string) {
    return `数据来自 ${url}`;
  }
}

new DemoService().fetchData('https://api.harmonyos.com');

🧠 发生了什么?

  • 编译器在看到@Log时,会调用Log函数;
  • target 是类的原型;
  • propertyKey 是方法名;
  • descriptor 是方法的描述对象。

于是,我们就能在函数执行前后加点“料”——打印日志、检查权限、测耗时,你说爽不爽?😎

🔧 二、参数与元数据 —— “装饰器界”的心灵感应

光会加日志还不够,我们得让装饰器更有智慧,能记住、能传参、还能玩点元数据魔法。
在ArkTS中,装饰器支持“工厂模式”,我们可以把装饰器写成一个能接收参数的函数工厂。

来个例子,让我们自己定义一个“权限检查装饰器”:

// demo2: 带参数的装饰器
function RequireRole(role: string) {
  return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    const original = descriptor.value;
    descriptor.value = function (...args: any[]) {
      const userRole = this.currentUser?.role || 'guest';
      if (userRole !== role) {
        throw new Error(`⛔ 无访问权限!需要角色: ${role}`);
      }
      return original.apply(this, args);
    };
    return descriptor;
  };
}

class UserService {
  currentUser = { name: 'Tom', role: 'guest' };

  @RequireRole('admin')
  deleteUser(id: number) {
    console.log(`用户 ${id} 已被删除`);
  }
}

const service = new UserService();
service.deleteUser(123); // ❌ 抛错:无访问权限!

🪄 是不是有点意思?
这时候的装饰器不只是“美化”,而是真正参与了业务逻辑判断。

💡 延伸:ArkTS中的元数据(Metadata)
ArkTS支持通过reflect-metadata库(或HarmonyOS自带的反射API)来为装饰器保存额外信息:

import 'reflect-metadata';

function Model(name: string) {
  return function (target: Function) {
    Reflect.defineMetadata('modelName', name, target);
  };
}

@Model('User')
class User {}

console.log(Reflect.getMetadata('modelName', User)); // 输出: "User"

这种机制让我们能在运行时“打捞”出类型信息、注解配置,非常适合ORM、DI框架这种“元编程场景”。


🧠 三、高级玩法 —— 编译器钩子 + 自定义 @CustomDecorator

到了这里,如果你还只是写@Log@Cache这种“温柔型”装饰器,那你可太低调了!😏
我们要上点高级货——自定义编译器钩子(Compiler Hook)+ @CustomDecorator

HarmonyOS的ArkTS编译器提供了一套可扩展的装饰器钩子机制
这意味着你可以注册自己的装饰器处理逻辑,让编译器在构建时“帮你做事”。

示例:
我们写一个@AutoBind装饰器,它能自动把类方法绑定到当前实例(避免this丢失的经典bug)。

// demo3: 自定义编译器钩子装饰器
function AutoBind(target: any, key: string, descriptor: PropertyDescriptor) {
  const original = descriptor.value;
  return {
    configurable: true,
    get() {
      const boundFn = original.bind(this);
      Object.defineProperty(this, key, {
        value: boundFn,
        configurable: true,
        writable: true,
      });
      return boundFn;
    },
  };
}

class ViewModel {
  name = 'ArkUI Dev';

  @AutoBind
  printName() {
    console.log(`Hello, ${this.name}`);
  }
}

const vm = new ViewModel();
const fn = vm.printName;
fn(); // ✅ 正确输出:Hello, ArkUI Dev

🚀 编译器如何处理?
在ArkTS编译阶段,编译器会检测@AutoBind修饰符,并在生成字节码时执行对应的钩子逻辑。
如果你在HarmonyOS SDK的build-profile.json5里开启装饰器编译选项,还能将其作为预处理插件运行。


🎯 四、小结 & 彩蛋

到这里,我们从“装饰器的原理”一路聊到了“元数据机制”再到“编译器钩子”。
是不是有种“原来这糖衣炮弹这么有深度”的感觉?😄

装饰器不是魔法,但能让代码看起来像魔法。
它让我们能在逻辑之外,书写优雅的抽象。
无论你是写ArkUI组件、系统服务,还是构建自定义框架,
都可以通过@CustomDecorator的方式,让代码变得更灵动、更有表达力。

❤️ 最后送你一句话:
“写代码的最高境界,不是功能跑通,而是让人看了想鼓掌。”👏

(未完待续)

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐