JS中装饰器到底是什么?

因主要的技术栈是Angular,对于Angular采用的装饰器特别认可,是一种优雅的拦截JS的方式。

TC39的装饰器提案其实共3个:class类和类属性装饰器、function函数装饰器、parameter方法参数装饰器,后2个仍处于Stage 0中,因此本文只针对class装饰器

目前Decorator仍处于Stage 2的阶段,不知道能否在ES2019(ES10)中推出,但一个提案只要能进入Stage 2,就基本会包括在以后的正式标准里面。

有N多文章写道Decorator是ES2016(ES7)推出的,不知道这是从哪里流传出来的,ES2016最终特性根本就没有Decorator,可能的原因:Decorator只是有望在ES2016推出的,实际上并没有。

image

以Angular中的一个组件为例,来说明装饰器的主要用法和装饰器到底是什么:

@Component({
  selector: 'app-transfer-common',
  templateUrl: './transfer-common.component.html',
  styleUrls: ['./transfer-common.component.scss']
})
@AutoUnsubscribe()
export class TransferCommonComponent implements OnInit {
  whichRouter: String;
  btnShowStatus: ShowOrHideBtn;
  queryParamsSubscribe: any;
  userInfo: any = this.commonUserService.getUserInfo(); 
  i18ns_common;
  constructor(
    private router: Router,
    private route: ActivatedRoute,
    private returnVariousBtn: ReturnVariousRouterParamsService,
    private commonUserService: CommonUserService,
    private httpService: HttpService,
    private translate: TranslateService,
  ) { }

  ngOnInit() {
  }

  // 跳转到新增
  @getProperty
  gotoAdd() {
  }

  /* ngOnDestroy() {
    this.queryParamsSubscribe.unsubscribe();

  } */
}

一.类装饰器

AutoUnsubscribe是一个自动取消订阅的装饰器,可传入参数指定某个可订阅对象不自动取消订阅。如不需要指定,可取消外层高阶函数

export function AutoUnsubscribe(params: string) {
  return function (constructor) {
    console.log(constructor);
    console.log(constructor.prototype);
    const originNgDestory = constructor.prototype.ngOnDestroy;
    constructor.prototype.ngOnDestroy = function () {
      console.log(this);
      console.log(this.constructor);
      console.log(constructor);
      for (const property of Object.values(this)) {
        if (property && (typeof property.unsubscribe === 'function')) {
          console.log(property);
        }
      }
      originNgDestory && typeof originNgDestory === 'function' && originNgDestory.apply(this);
    };
  };
}

1.类装饰器仅仅是一个接受一个参数的、被装饰的类的构造函数,常用于修改、添加类的原型方法

2.类装饰器传入的constructor就是类的constructor(特指constructor(){}),如下图红色部分.类装饰器作用于类的 constructor,并且观察、修改或者替换一个类的定义。

3.this是整个class,如图天蓝色外框
4.特殊的是:类的属性方法在类的proto中,其他的是类的直接子集,如图靑蓝色部分

4.参数constructor === this.constructor

5.执行订阅的时候,订阅的是一个Subscribe对象,property.unsubscribe是从原型链上获取的unsubscribe,如下图绿色部分


image

二.类属性(方法)装饰器

// 下一个事件队列中执行的装饰器
export const timeoutDecorator = function (milliseconds: number = 0) {
  return function (target, key, descriptor: any) {
    console.log(target);  // 即整个对象
    console.log(key);     // 装饰器修饰的某个key
    console.log(descriptor);
    const originalMethod = descriptor.value;  // value即这个key对应的方法
    
    /** 当执行gotoAdd方法时,即先执行以下函数 */
    descriptor.value = function (...args) {
      console.log(args);
      setTimeout(() => {
        originalMethod.apply(this, args);       // gotoAdd方法调用
      }, milliseconds);
    };
    console.log(descriptor);
    return descriptor;
  };
};

export const getProperty = function (target, name, descriptor) {
  console.log(target);
  console.log(name);
  console.log(descriptor);
  const originalMethod = descriptor.value;
  descriptor.value = function (...args) {
    console.log(args);
    originalMethod.apply(this, args);
  };
  console.log(descriptor);
  return descriptor;
};

image

1.类方法装饰器是一个函数,函数参数就是Object.defineProperty中的三个参数即:target(目标对象)、key(调用的属性名)、descriptor(调用的属性的描述,包括configurable、enumerable、writable、value)

2.类方法装饰器的作用是把类中的方法放入装饰器中执行,个人理解类似于管道或者拦截器

3.类的普通属性(不是方法的)也可以添加装饰器,比如Angular中的@ViewChild,@ViewContent

三.修饰器本质就是编译时执行的函数

注意,不管哪种修饰器,对类的行为的改变,是代码编译时(初始化时)发生的,而不是在运行时。这意味着,修饰器能在编译阶段运行代码。也就是说,修饰器本质就是编译时执行的函数。

表现上:添加上装饰器后,装饰器中descriptor.value = function(...args) ...... 函数外的内容会先执行.然后在每次触发类中的修饰的方法是会才会调用descriptor.value = function(...args) ...... 中的内容,args是指触发此方法时的实际参数.再通过apply方法,调用需要修饰的类方法,注意不一定是指向this,也有可能是target,具体看情况

四.特别注意:this

箭头函数中的this和普通函数的this是不同的,注意函数体内是否有this,有的话就不要用箭头函数了,详情请搜索箭头函数的this


划重点

1.类装饰器和类方法装饰器是不同的,但本质都是一个函数
2.类装饰器常用于修改、添加类的原型方法,类方法装饰器用于拦截类方法,需要掌握具体写法
3.注意箭头函数和this的搭配使用问题

参考资料:

ECMAScript 6 入门

ECMAScript proposals

decorator

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容