博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Angular 2 Decorators - 1
阅读量:5950 次
发布时间:2019-06-19

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

在我们深入了解 Angular 2 中 @NgModule、@Component、@Injectable 等常见的装饰器之前,我们要先了解 TypeScript 中的装饰器。装饰器是一个非常酷的特性,最早出现在 Google 的 AtScript 中,它出现的目的是为了让开发者,开发出更容易维护、更容易理解的 Angular 代码。令人兴奋的是,在2015年 Angular 团队跟 MicroSoft 的 TypeScript 团队经过数月的的交流,最终决定采用 TypeScript 来重写 Angular 2 项目 。

装饰器是什么

  • 它是一个表达式

  • 该表达式被执行后,返回一个函数

  • 函数的入参分别为 targe、name 和 descriptor

  • 执行该函数后,可能返回 descriptor 对象,用于配置 target 对象 

装饰器的分类

  • 类装饰器 (Class decorators)

  • 属性装饰器 (Property decorators)

  • 方法装饰器 (Method decorators)

  • 参数装饰器 (Parameter decorators)

TypeScript 类装饰器

类装饰器声明:

declare type ClassDecorator = 
(target: TFunction) => TFunction | void

类装饰器顾名思义,就是用来装饰类的。它接收一个参数:

  • target: TFunction - 被装饰的类

看完第一眼后,是不是感觉都不好了。没事,我们马上来个例子:

function Greeter(target: Function): void {  target.prototype.greet = function (): void {    console.log('Hello!');  }}@Greeterclass Greeting {  constructor() {    // 内部实现  }}let myGreeting = new Greeting();myGreeting.greet(); // console output: 'Hello!';

上面的例子中,我们定义了 Greeter 类装饰器,同时我们使用了 @Greeter 新的语法,来使用装饰器。

(备注:读者可以直接复制上面的代码,在 TypeScript Playground 中运行查看结果)。

有的读者可能想问,例子中总是输出 Hello! ,能自定义输出的问候语么 ?这个问题很好,答案是可以的。具体实现如下:

function Greeter(greeting: string) {  return function(target: Function) {    target.prototype.greet = function(): void {      console.log(greeting);    }  }}@Greeter('您好')class Greeting {  constructor() {    // 内部实现  }}let myGreeting = new Greeting();myGreeting.greet(); // console output: '您好!';

TypeScript 属性装饰器

属性装饰器声明:

declare type PropertyDecorator = (target:Object, propertyKey: string | symbol ) => void;

属性装饰器顾名思义,用来装饰类的属性。它接收两个参数:

  • target: Object - 被装饰的类

  • propertyKey:string | symbol - 被装饰类的属性名

趁热打铁,马上来个例子热热身:

function LogChanges(target: Object, key: string) {  var propertyValue: string = this[key];  if(delete this[key]) {    Object.defineProperty(target, key, {      get: function () {        return propertyValue;      },      set: function(newValue) {        propertyValue = newValue;        console.log(`${key} is now ${propertyValue}`);      }    });  }}class Fruit {  @LogChanges  name: string;}let fruit = new Fruit();fruit.name = 'apple'; // console output: 'name is now apple'fruit.name = 'banana'; // console output: 'name is now banana'

那么问题来了,如果用户想在属性变化的时候,自动刷新页面,而不是简单地在控制台输出消息,那要怎么办?我们能不能参照类装饰器自定义问候语的方式,来实现监测属性变化的功能。具体实现如下:

function LogChanges(callbackObject: any) {  return function(target: Object, key: string): void {    var propertyValue: string = this[key];      if(delete this[key]) {        Object.defineProperty(target, key, {          get: function () {              return propertyValue;          },          set: function(newValue) {              propertyValue = newValue;              callbackObject.onchange.call(this, propertyValue);          }     });    }  }}class Fruit {  @LogChanges({    onchange: function(newValue: string): void {      console.log(`The fruit is ${newValue} now`);    }  })  name: string;}let fruit = new Fruit();fruit.name = 'apple'; // console output: 'The fruit is apple now'fruit.name = 'banana'; // console output: 'The fruit is banana now'

TypeScript 方法装饰器

方法装饰器声明:

declare type MethodDecorator = 
(target:Object, propertyKey: string | symbol, descriptor: TypePropertyDescript
) => TypedPropertyDescriptor
| void;

方法装饰器顾名思义,用来装饰类的属性。它接收三个参数:

  • target: Object - 被装饰的类

  • propertyKey: string | symbol - 方法名

  • descriptor: TypePropertyDescript - 属性描述符

废话不多说,直接上例子:

function LogOutput(tarage: Function, key: string, descriptor: any) {  var originalMethod = descriptor.value;  var newMethod = function(...args: any[]): any {    var result: any = originalMethod.apply(this, args);    if(!this.loggedOutput) {      this.loggedOutput = new Array
(); } this.loggedOutput.push({ method: key, parameters: args, output: result, timestamp: new Date() }); return result; }; descriptor.value = newMethod;}class Calculator { @LogOutput double (num: number): number { return num * 2; }}let calc = new Calculator();calc.double(11);// console ouput: [{method: "double", output: 22, ...}]console.log(calc.loggedOutput);

最后我们来看一下参数装饰器:

TypeScript 参数装饰器

参数装饰器声明:

declare type ParameterDecorator = (target: Object, propertyKey: string | symbol, parameterIndex: number ) => void

参数装饰器顾名思义,是用来装饰函数参数,它接收三个参数:

  • target: Object - 被装饰的类

  • propertyKey: string | symbol - 方法名

  • parameterIndex: number - 方法中参数的索引值

function Log(target: Function, key: string, parameterIndex: number) {  var functionLogged = key || target.prototype.constructor.name;  console.log(`The parameter in position ${parameterIndex} at ${functionLogged} has    been decorated`);}class Greeter {  greeting: string;  constructor(@Log phrase: string) {    this.greeting = phrase;   }}// console output: The parameter in position 0 at Greeter has// been decorated

我有话说

1.Object.defineProperty() 方法有什么用 ?

Object.defineProperty 用于在一个对象上定义一个新的属性或者修改一个已存在的属性,并返回这个对象。 方法的签名:Object.defineProperty(obj, prop, descriptor) ,参数说明如下:

  • obj 需要定义的属性对象

  • prop 需被定义或修改的属性名

  • descriptor 需被定义或修改的属性的描述符

对象里目前存在的属性描述符有两种主要形式:数据描述符和存取描述符。数据描述符是一个拥有可写或不可写值的属性。存取描述符是由一对 getter-setter 函数功能来描述的属性。描述符必须是两种形式之一,不能同时是两者。

数据描述符和存取描述符均具有以下可选键值:

  • configurable

    当且仅当该属性的 configurable 为 true 时,该属性才能够被改变,也能够被删除。默认为 false。

  • enumerable

    当且仅当该属性的 enumerable 为 true 时,该属性才能够出现在对象的枚举属性中。默认为 false。

数据描述符同时具有以下可选键值:

  • value

    该属性对应的值。可以是任何有效的 JavaScript 值(数值,对象,函数等)。默认为 undefined。

  • writable

    当且仅当仅当该属性的writable为 true 时,该属性才能被赋值运算符改变。默认为 false。

存取描述符同时具有以下可选键值:

  • get

    一个给属性提供 getter 的方法,如果没有 getter 则为 undefined。该方法返回值被用作属性值。默认为undefined。

  • set

    一个给属性提供 setter 的方法,如果没有 setter 则为 undefined。该方法将接受唯一参数,并将该参数的新值分配给该属性。默认为undefined。

使用示例:

var o = {}; // 创建一个新对象Object.defineProperty(o, "a", {value : 37, writable : true, enumerable : true,       configurable : true});

总结

本文主要介绍了 TypeScript 中的四种装饰器,了解装饰器的基本分类和实现原理,为我们下一篇深入 Angular 2 的 @NgModule、@Component、@Injectable 等常用装饰器做好铺垫。

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

你可能感兴趣的文章
python \uxxxx转中文,Python列表中的字典 \uxxxx转中文,
查看>>
tomcat配置访问项目时不需要加项目名称
查看>>
转JMeter 利用Jmeter批量数据库插入数据
查看>>
在多模块开发的时候,利用项目继承可以将结构信息、部署信息,将共同的依赖放在一个父类中。...
查看>>
[原创] IAR7.10安装注册教程
查看>>
Java图像渐变
查看>>
解决ios下的微信打开的页面背景音乐无法自动播放(转载)
查看>>
fidder设置断点,修改请求参数等
查看>>
Material Design之RecyclerView的使用(一)
查看>>
系统编程是什么
查看>>
git rebase简介(基本篇)
查看>>
Backup and Recovery Basics1
查看>>
C语言各种keyword
查看>>
Rescue
查看>>
1775. [国家集训队2010]小Z的袜子
查看>>
前端学习 -- Html&Css -- 表单
查看>>
android的toogleButton和switch的使用方法
查看>>
CSS网页布局垂直居中整理
查看>>
HTML标签列表
查看>>
由“从按下回车到网页显示”粗谈网页优化
查看>>