博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
浅入浅出Typescript Decorators
阅读量:6199 次
发布时间:2019-06-21

本文共 4802 字,大约阅读时间需要 16 分钟。

临时起的兴趣,想写一篇关于ts decorator的文章,就花小半天整理了一下...

这东西,在ES2017里好像也有... 文档的话看。
因为临时,也没想写太多文字介绍,带少许文字说明直接开撸代码吧。

本文通过ts编译后的decorator代码解释一番装饰器是什么?能做什么?有什么好处?

实现代码

编译后代码是这样的,带注释:

var __decorate =  (this && this.__decorate) ||  function(decorators, target, key, desc) {    // c 参数长度    // r ? c < 3 则是target,否则先判断desc为null的话则将desc取target的key属性的描述,再否则便是desc了    // d 预留使用    var c = arguments.length,      r =        c < 3          ? target          : desc === null          ? (desc = Object.getOwnPropertyDescriptor(target, key))          : desc,      d;    // 下面文字解释,这仅是个甩锅的行为    if (typeof Reflect === "object" && typeof Reflect.decorate === "function")      r = Reflect.decorate(decorators, target, key, desc);    // 循环 decorators  并每次赋值给 d,并且判断值    else      for (var i = decorators.length - 1; i >= 0; i--)        if ((d = decorators[i]))          // c < 3 ,用 r 作为 decorators[i] 的入参执行;          // c > 3 ,target, key, r 作为 decorators[i] 的入参执行;          // c === 3,target, key 作为 decorators[i] 的入参执行。          // 如果执行无返回结果, r = r;。          r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;    // 如果 c > 3 && r , 修改 target ,返回 r    return c > 3 && r && Object.defineProperty(target, key, r), r;  };

从代码里可以看出,最终结果要么是用decorator执行target,从而改变一些什么东西;要么就是使用Object.defineProperty来对target来做操作,代码就几行,用处确不小...具体的执行过程结合下面的两个例子会更容易理解。

值得一提的是,关于代码里的Reflect原本以为是这个 里的方法,但可惜不是;

然后猜测是Typescript的实现,翻了的代码(如果打不开链接就从 node_modules 下看吧),发现也不是,再去查 stackoverflow 的解释,是这样的

大致说是ts希望把这个锅甩给ES来补,到时候ts的else里的代码便是polyfill了

案例

以下面的 decorator 和 class 作为例子解释

// ts 代码function show(target: any) {  console.log(target);  target.prototype.showMe = (name: string) => {    console.log("show me :", name);  };}interface IShow {  showMe?(name: string): any;}@showclass Show implements IShow {  showMe(name: string) {}}const shoow = new Show();shoow.showMe("ys");// 编译后的js// decorator ,简单的打印,并且修改方法function show(target) {  console.log(target);  target.prototype.showMe = function(name) {    console.log("show me :", name);  };}// class Shoowvar Shoow = (function() {  function Shoow() {}  Shoow.prototype.showMe = function(name) {};  // decorators 为[show],target 为 Shoow  Shoow = __decorate([show], Shoow);  return Shoow;})();var shooow = new Shoow();shooow.showMe("ys");// output : show me : ys

理解一下执行步骤:

  1. decorators = [show],target = Shoow,
  2. c = 2,r = target{Shoow},d = undefined
  3. 不存在 Reflect,走循环,只循环一次
  4. d = show,r = show(target{Shoow}),r 没返回结果,所以 r 还是 r , r = target{Shoow}
  5. return 结果: c = 2, 所以返回 false
  6. 执行后无返回值,但是在执行show(target{Shoow})的时候将showMe方法改掉了,于是执行结果符合预期

一个不够?再来一个?这次在里面返回一个函数试试?

// ts代码function logger1(config?) {  return function(target, key: string, descriptor: PropertyDescriptor) {    const _value = descriptor.value;    if (typeof _value === "function") {      descriptor.value = (...args) => {        console.log(`logger1-begin : ${config.level}`);        const res = _value.apply(target, args);        console.log(`logger1-end`);        return res;      };    }    return descriptor;  };}function logger2(config?) {  return function(target, key: string, descriptor: PropertyDescriptor) {    const _value = descriptor.value;    if (typeof _value === "function") {      descriptor.value = (...args) => {        console.log(`logger2-begin : ${config.level}`);        const res = _value.apply(target, args);        console.log(`logger2-end`);        return res;      };    }    return descriptor;  };}interface IShow {  showMe?(name: string): any;}class Show implements IShow {  @logger1({ level: "info" })  @logger2({ level: "error" })  showMe(name: string) {    console.log("show me :", name);  }}const shoow = new Show();shoow.showMe("ys");// output 这里手动加个缩进,这时候showMe方法已经经过多次包裹// logger1-begin : info//   logger2-begin : error//     show me : ys//   logger2-end// logger1-end

再来看看执行步骤:

  1. decorators = [logger1, logger2],target = Shoow,key = "showMe",desc = null 注意,这里是为null,不是为undefined
  2. c = 4,r = target{Shoow},d = undefined
  3. 不存在 Reflect,走循环,只循环一次
  4. 第一次循环取 d = logger1,r = logger1(target, key, r),因为 return 存在值,r = logger1 的返回函数,这时候 descriptor.value 被第一次重写
  5. 第二次循环取 d = logger2,r = logger2(target, key, r),又因为 return 存在值,r = logger2 的返回函数,这时候 descriptor.value 被第二次重写
  6. return 结果: 因为 c > 3,r 存在值,执行 Object.defineProperty(target, key, r)来重写对象属性并且返回 r (r为重写的结果)
  7. 经过 2 次重写 showMe 属性值,执行结果符合预期

欢乐

装饰器给你带来什么欢乐?简单列几个最明显的优点,其他在运用中各自提取每日份的快乐去吧...

  1. 业务和功能之间的解耦(比如日志)
// 日常  dosomething(){    consol.log('start')    // some thing    console.log('end')  }  // 使用装饰器  @logger(logConfig?)  dosomething();
  1. 代码结构清晰(特别针对多层HOC后的React组件)
// 日常多层HOC  class MyComponent extends Component{    // ..  }  connect(mapFn)(    MyHoc(someMapFn)(      Form.create(fieldsMapFn)(MyComponent)    )  )  // 使用装饰器  @connect(mapFn)  @MyHoc(someMapFn)  @FormFields(fieldsMapFn)  class MyComponent extends Component{    // ..  }  export default MyComponent;

最后

AOP,了解一下

转载地址:http://wpnca.baihongyu.com/

你可能感兴趣的文章
java.lang.OutOfMemoryError: Java heap space 解决方法
查看>>
CentOS6.0 x64 位将光盘作为yum源
查看>>
ubuntu 14.04 root用户登录
查看>>
MySQL性能优化的最佳20+条经验
查看>>
为什么使用Erlang?
查看>>
CodeIgniter Disallowed Key Characters
查看>>
浅谈iPhone OS(iOS)架构
查看>>
java反射详解 (一)
查看>>
AngularJs学习日记[4]:AngularJS表达式【2】
查看>>
Spring Security
查看>>
Linux系统管理-crond、chkconfig、systemd、unit、target
查看>>
记一次iptables实例
查看>>
鲍勃·迪伦音乐作品列表
查看>>
Java中public,private,final,static等概念的解读
查看>>
nexus-2.5安装(图文)
查看>>
4行CSS实现表格内容超过一行的部分,用省略号代替
查看>>
Ubuntu下LAMP环境搭建 Apache、MySQL、PHP
查看>>
记一次性能压测的问题排查过程
查看>>
GlbLib函数库手册
查看>>
spring security3.x学习(2)_认证和授权&运行第一个项目
查看>>