临时起的兴趣,想写一篇关于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,并且判断值elsefor (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 ,返回 rreturn c > 3 && r && Object.defineProperty(target, key, r), r;};

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

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

然后猜测是Typescript的实现,翻了Typescript/tsc.js的代码(如果打不开链接就从 node_modules 下看吧),发现也不是,再去查 stackoverflow 的解释,是这样的 what-is-reflect-decorate-in-js-code-transpiled-from-ts

大致说是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;
}@show
class 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 Shoow
var Shoow = (function() {function Shoow() {}Shoow.prototype.showMe = function(name) {};// decorators 为[show],target 为 ShoowShoow = __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 thingconsole.log('end')}// 使用装饰器@logger(logConfig?)dosomething();
  1. 代码结构清晰(特别针对多层HOC后的React组件)
  // 日常多层HOCclass 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,了解一下

浅入浅出Typescript Decorators相关推荐

  1. SegmentFault 技术周刊 Vol.16 - 浅入浅出 JavaScript 函数式编程

    函数式编程(Functional Programming),一看这个词,简直就是学院派的典范. 以至于从 Lisp 的创世,到 Scheme.Haskell.Clean.Erlang.Miranda. ...

  2. SegmentFault 技术周刊 Vol.16 - 浅入浅出 JavaScript 函数式编程 1

    函数式编程(Functional Programming),一看这个词,简直就是学院派的典范. 以至于从 Lisp 的创世,到 Scheme.Haskell.Clean.Erlang.Miranda. ...

  3. [科普]浅入浅出Liunx Shellcode

    创建时间:2008-05-13 文章属性:原创 文章提交: pr0cess  (pr0cess_at_cnbct.org) 浅入浅出Liunx Shellcode /*---------------- ...

  4. 浅入深出之Java集合框架(上)

    Java中的集合框架(上) 由于Java中的集合框架的内容比较多,在这里分为三个部分介绍Java的集合框架,内容是从浅到深,如果已经有java基础的小伙伴可以直接跳到浅入深出之Java集合框架(下). ...

  5. 浅入深出之Java集合框架(中)

    Java中的集合框架(中) 由于Java中的集合框架的内容比较多,在这里分为三个部分介绍Java的集合框架,内容是从浅到深,如果已经有java基础的小伙伴可以直接跳到浅入深出之Java集合框架(下). ...

  6. Angular浅入深出系列 - 写在前面

    本系列目录: 写在前面 基础知识 控制器(Controller) 作用域(Scope) 集合(Collection) 模块(Module) 依赖注入(Dependency Injection) 服务( ...

  7. 浅入浅出深度学习理论实践

    全文共9284个字,40张图,预计阅读时间30分钟. 前言 之前在知乎上看到这么一个问题:在实际业务里,在工作中有什么用得到深度学习的例子么?用到 GPU 了么?,回头看了一下自己写了这么多东西一直围 ...

  8. 浅入浅出 Android 安全 翻译完成!

    浅入浅出 Android 安全 翻译完成! 作者:Yury Zhauniarovich 译者:飞龙 来源:Yury Zhauniarovich | Publications 在线阅读 PDF格式 EP ...

  9. 浅入深出Vue:环境搭建

    浅入深出Vue:环境搭建 工欲善其事必先利其器,该搭建我们的环境了. 安装NPM 所有工具的下载地址都可以在导航篇中找到,这里我们下载的是最新版本的NodeJS Windows安装程序 下载下来后,直 ...

  10. 处理中文乱码_浅入深出:一次提问引发的深思,从此再也不怕“乱码”问题

    这是恋习Python之浅入深出系列第3篇原创首发文章 作者|丁彦军 来源|恋习Python(ID:sldata2017) 转载请联系授权(微信ID:2394608316) 近日,有位粉丝向我请教,在爬 ...

最新文章

  1. Android studio怎么找到当前文件在电脑路径位置
  2. 恭喜!中科大少年班放榜:2020年全国录取48人
  3. Go Web 开发(一)
  4. 测试过程之过分关注功能性测试
  5. 收件箱的邮箱发件人可以批量导出吗_企业邮箱常用技巧,高效处理邮件
  6. bootstrap-fileinput 添加打印按钮
  7. [原]unity3d ios平台内存优化(一)
  8. delphi 打印指定地点文件_2020年度电脑、打印机耗材及相关配件采购招标公告
  9. 【渝粤题库】国家开放大学2021春1258房屋建筑混凝土结构设计题目
  10. 基于androidx的快速开发框架_Vue企业级优雅实战07框架开发03封装基于MockJS的模拟数据...
  11. mysql技术内幕sampdb_MySql技术内幕之MySQL入门(1)
  12. 内容页响应母版页控件的事件
  13. 关于node.js杂记
  14. UVA 12898 - And Or 与和或 (思路题)
  15. if 语句 写了return 报错
  16. 词表匹配工具FlashText
  17. LeetCode(数据库)- 2142. The Number of Passengers in Each Bus I
  18. python中def main是什么意思_Python main()函数解析
  19. c#上位机plc通讯读位
  20. 2022年1月语音合成(TTS)和语音识别(ASR)论文月报

热门文章

  1. 以非泛型方式调用泛型方法(三)
  2. Ubuntu 16.04安装Memcached(单机)
  3. 研究Mysql优化得出一些建设性的方案
  4. linux下GPRS模块的应用程序
  5. AndroidManifest.xml文件详解(application)
  6. 数据库备份还原顺序关系(环境:Microsoft SQL Server 2008 R2)
  7. 解决php的It is not safe to rely on the system’s timezone settings的问题
  8. 24/100. Linked List Cycle
  9. 关于搜狐焦点房产的数据分析
  10. jquery css 定义背景不重复