从C#到TypeScript - 装饰器
总目录
从C#到TypeScript - 装饰器
在C#里面如果想要不直接修改类或方法,但给类或方法添加一些额外的信息或功能,可以想到用Attribute
,这是一个十分方便的功能装饰器。
用TypeScript同样也可以利用装饰器来给类、函数、属性以及参数添加附加功能,装饰器是ES7的一个提案,在TypeScript里已经有实现可用,不过需要在tsconfig.json
里启用experimentalDecorators
。
"compilerOptions": {
..., // other options
"experimentalDecorators": true
}
装饰器介绍
TypeScript中装饰器可以应用到类、方法、属性及函数参数上,而且可以同时应用多个。
装饰器的写法是@name()
,()
可以不要,也可以在里面写一些参数。
@Testable
@Log('controller')
class Controller{
@GET
getContent(@QueryParam arg: string): string{
return '';
}
}
装饰器的实现
装饰器根据实现可以分两种:
一种是不带括号,和属性一样,如@Testable
。
function Testable(target: Function) { // 类、方法、属性、方法参数的参数各不相同
//这里可以记录一些信息到target,或者针对target做一些处理,如seal
}
另外一种是带括号的,和函数一样,如@Log('controller')
,实现函数里的参数就是括号里的参数,而且需要返回一个function
。
function Log(name: string) { // name就是传进来的参数'controller'
return function(target: Function) { // 类、方法、属性、方法参数的参数各不相同
// 这里可以根据name和target来做一些处理
}
}
类装饰器
上面的(target: Function)
其实就是类的装饰器参数,指向的是类的构造函数,如果想给类加一个简单的seal功能,可以这样做:
function sealed(target: Function) {
Object.seal(target);
Object.seal(target.prototype);
}
@sealed
class Test{
}
Test.prototype.test = ''; // 运行时出错,不能添加
上面的sealed
就是类的装饰器,target
指构造函数,类装饰器就这么一个参数。
方法装饰器
方法装饰器的使用方法和类装饰器类似,只是参数不一样,方法装饰器有三个参数:
- 如果装饰的是静态方法,则是类的构造函数,如果是实例方法则是类的原型。
- 方法的名字。
- 方法的
PropertyDescriptor
。
PropertyDescriptor
即属性描述符,有
configurable 是否可以配置,如动态添加删除函数属性之类
writable 是否可写,可以用来设置只读属性
enumerable 是否可枚举,即是否能在for...in
中能枚举到
value 对象或属性的值
有了这些参数就可以很好的给方法添加一些功能,比如下面实现类型WebApi里的Get的路由:
const Router = Symbol(); // 唯一key,用来存装饰器的信息
function GET(path?: string) { // GET带了个可选参数
return (target: any, name: string) => setMethodDecorator(target, name, 'GET', path);
}
//把method和path存起来,路由查找的时候就可以用了
function setMethodDecorator(target: any, name: string, method: string, path?: string){
target[Router] = target[Router] || {};
target[Router][name] = target[Router][name] || {};
target[Router][name].method = method;
target[Router][name].path = path;
}
// 通过PropertyDescriptor来设置enumerable
function Enumerable(enumerable: boolean) {
return (target: any, name: string, descriptor: PropertyDescriptor) => {
descriptor.enumerable = enumerable;
};
}
class Controller{
@GET
@Enumerable(true)
getContent(arg: string): string{
return '';
}
}
参数装饰器
方法参数同样可以有装饰器,同样有三个参数,前两个参数和方法的一致,最后一个参数是所装饰的参数的位置。
能过参数装饰器可以给方法动态的检查或设置参数值,下面是检查参数是否为空,为空则抛出异常。
const CheckNullKey = Symbol();
const Router = Symbol();
// 把CheckNull装饰的参数存起来
function CheckNull(target: any, name: string, index: number) {
target[Router] = target[Router] || {};
target[Router][name] = target[Router][name] || {};
target[Router][name].params = target[Router][name].params || [];
target[Router][name].params[index] = CheckNullKey;
}
// 找出CheckNull的参数,并检查参数值,为空则抛异常,否则继续执行方法
function Check(target: any, name: string, descriptor: PropertyDescriptor) {
let method = descriptor.value;
descriptor.value = function () {
let params = target[Router][name].params;
if (params) {
for (let index = 0; index < params.length; index++) {
if (params[index] == CheckNullKey && // 找到CheckNull的参数并抛异常
(arguments[index] === undefined || arguments[index] === null)) {
throw new Error("Missing required argument.");
}
}
}
return method.apply(this, arguments);
}
}
class Controller{
@Check
getContent(@CheckNull id: string): string{
console.info(id);
return id;
}
}
new Controller().getContent(null); // error : Missing required argument.
属性装饰器
用法同上,参数只有两个,和类装饰器的前两个一样,常用来标识属性的特性。
function Column(target: any, name: string) {
//把name存起来,这个column仅仅是标识出来对应数据库中的列,常用在ORM框架中
}
class Table{
@Column
name: string;
}
另外还有属性访问器的装饰器,和方法基本一样,同样的三个参数,不过同个属性的get
和set
只能有一个有,而且必须是先声明的那个。
class User {
private _name: string;
@Enumerable(true)
get name(){
return this._name;
}
set name(value: string) {
this._name = value;
}
}
多个装饰器的执行顺序
一个声明可以添加多个装饰器,所以会有个执行先后顺序。
首先从上到下执行装饰器函数,然后再从下往上应用带括号的装饰器返回的函数。
function Test1(){
console.info('eval test1');
return function(target: any, name: string, descriptor: PropertyDescriptor){
console.info('apply test1');
}
}
function Test2(){
console.info('eval test2');
return function(target: any, name: string, descriptor: PropertyDescriptor){
console.info('apply test2');
}
}
class User1{
@test1()
@Test2()
getName(){
}
}
结果是:
eval test1
eval test2
apply test2
apply test1
总之,装饰器等于引入了天然的装饰模式,给类,方法等添加额外功能。不过装饰器目前还不算太稳定,但是由于确实方便,已经有成熟项目在使用了。
从C#到TypeScript - 装饰器的更多相关文章
- 基于TypeScript装饰器定义Express RESTful 服务
前言 本文主要讲解如何使用TypeScript装饰器定义Express路由.文中出现的代码经过简化不能直接运行,完整代码的请戳:https://github.com/WinfredWang/expre ...
- Angular 个人深究(一)【Angular中的Typescript 装饰器】
Angular 个人深究[Angular中的Typescript 装饰器] 最近进入一个新的前端项目,为了能够更好地了解Angular框架,想到要研究底层代码. 注:本人前端小白一枚,文章旨在记录自己 ...
- TypeScript装饰器(decorators)
装饰器是一种特殊类型的声明,它能够被附加到类声明,方法, 访问符,属性或参数上,可以修改类的行为. 装饰器使用 @expression这种形式,expression求值后必须为一个函数,它会在运行时被 ...
- TypeScript 装饰器
装饰器(Decorators)可用来装饰类,属性,及方法,甚至是函数的参数,以改变和控制这些对象的表现,获得一些功能. 装饰器以 @expression 形式呈现在被装饰对象的前面或者上方,其中 ex ...
- TypeScript 装饰器的执行原理
装饰器本质上提供了对被装饰对象 Property Descriptor 的操作,在运行时被调用. 因为对于同一对象来说,可同时运用多个装饰器,然后装饰器中又可对被装饰对象进行任意的修改甚至是替换掉实 ...
- typescript装饰器 方法装饰器 方法参数装饰器 装饰器的执行顺序
/* 装饰器:装饰器是一种特殊类型的声明,它能够被附加到类声明,方法,属性或参数上,可以修改类的行为. 通俗的讲装饰器就是一个方法,可以注入到类.方法.属性参数上来扩展类.属性.方法.参数的功能. 常 ...
- typescript装饰器定义 类装饰器 属性装饰器 装饰器工厂
/* 装饰器:装饰器是一种特殊类型的声明,它能够被附加到类声明,方法,属性或参数上,可以修改类的行为. 通俗的讲装饰器就是一个方法,可以注入到类.方法.属性参数上来扩展类.属性.方法.参数的功能. 常 ...
- TypeScript 中装饰器的理解?应用场景?
一.是什么 装饰器是一种特殊类型的声明,它能够被附加到类声明,方法, 访问符,属性或参数上 是一种在不改变原类和使用继承的情况下,动态地扩展对象功能 同样的,本质也不是什么高大上的结构,就是一个普通的 ...
- Typescript中的装饰器原理
Typescript中的装饰器原理 1.小原理 因为react中的高阶组件本质上是个高阶函数的调用, 所以高阶组件的使用,我们既可以使用函数式方法调用,也可以使用装饰器. 也就是说,装饰器的本质就是一 ...
随机推荐
- iOS所有常用证书,appID,Provisioning Profiles配置说明及制作图文教程
概述: 苹果的证书繁锁复杂,制作管理相当麻烦,今天决定重置一个游戏项目中的所有证书,做了这么多次还是感觉很纠结,索性直接记录下来,日后你我他查阅都方便: 首先得描述一下各个证书的定位,作用,这样在制作 ...
- iOS开发UITableView基本使用方法总结
本文为大家呈现了iOS开发中UITableView基本使用方法总结.首先,Controller需要实现两个delegate ,分别是UITableViewDelegate 和UITableViewDa ...
- 查看Linux连接数
netstat -ant | grep ESTABLISHED | wc -l
- IOS开发-UI学习-根据URL显示图片,下载图片的练习(button,textfield,image view,url,data)
编写一个如下界面,实现: 1.在文本输入框中输入一个网址,然后点击显示图片,图片显示到UIImageView中. 2.点击下载,这张显示的图片被下载到手机的Documents文件夹下的Dowmload ...
- IOS开发-UI学习-delegate(代理)的使用,键盘消失
代理是IOS开发中用到的一种设计模式.今天做了一个代理的小练习: 以下项目实现了两个页面之间的相互切换,并且在切换页面的时候完成了从一个页面往另一个页面的传值.从主页面往其他页面传值是容易的,但是反过 ...
- 笔记整理--玩转robots协议
玩转robots协议 -- 其他 -- IT技术博客大学习 -- 共学习 共进步! - Google Chrome (2013/7/14 20:24:07) 玩转robots协议 2013年2月8日北 ...
- PHP检测获取内存信息
PHP也可以检测获取到Windows的内存信息,而且代码还挺简单,无意发现的,觉得以后能用上,在此与大家分享. 本代码将得到总内存.初始使用等内存信息: <?php echo "初始: ...
- iOS 解决一个复杂bug 之 计分卡
由于该模块界面和业务逻辑都很复杂,并且整个界面设计和业务逻辑都在ViewController(下面简称为VC)里面完成.该VC共有3000多行,一个函数几百张的也有.所以,解决起来真是头疼. 1. 问 ...
- windows中如何查看某个端口被谁占用
说明:本人操作系统为win7 x64,文章转自http://jingyan.baidu.com/article/3c48dd34491d47e10be358b8.html,加上本人的注释. 开始--- ...
- 一个完整的PHP类包含的七种语法说明
类中的七种语法说明 -属性 -静态属性 -方法 -静态方法 -类常量 -构造函数 -析构函数 <?php class Student { // 类里的属性.方法和函数的访问权限有 (函数和方法是 ...