概念介绍
应用对象:类。
装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。
这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。
能够被附加到类声明、方法、访问符,属性或参数上,使用@expression的形式,@express求值后必须为一个函数,他会在运行时被调用,被装饰的声明信息作为PropertyDescriptor
参数传入。
使用
ts的编译环境
必须在命令行或`tsconfig.json`里启用`experimentalDecorators`编译器选项。
node TS的开发环境搭建
- package.json创建: npm init -y
- 开发依赖安装: npm i typescript ts-node-dev tslint @types/node -D
启动脚本
"scripts": {
"start": "ts-node-dev ./src/index.ts -P tsconfig.json --no-cache",
"build": "tsc -P tsconfig.json && node ./dist/index.js",
"tslint": "tslint --fix -p tsconfig.json"
}
加⼊tsconfig.json
{
"compilerOptions": {
"outDir": "./dist",
"target": "es2017",
"module": "commonjs",//组织代码⽅式
"sourceMap": true,
"moduleResolution": "node", // 模块解决策略
"experimentalDecorators": true, // 开启装饰器定义
"allowSyntheticDefaultImports": true, // 允许es6⽅式import
"lib": ["es2015"],
"typeRoots": ["./node_modules/@types"],
},
"include": ["src/**/*"] }
执行顺序
@f
@g
x
以上的写法相当于
f(g(x))
,当多个装饰器应用在一个声明上时会进行如下步骤的操作:
- 由上至下依次对装饰器表达式求值。
- 求值的结果会被当作函数,由下至上依次调用。
装饰器表达式会在运行时被当做函数调用,传入下列三个参数:
target
对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。propertyKey
成员的名字PropertyDescriptor
成员的描述符例子1
正常情况下,Maths类被实例化时候会执行log函数,log函数重写了add函数,所以函数的执行结果被改变了。``typescript console.log('日志应用和切面实现.....') function log(target, name, descriptor) { var oldValue = descriptor.value; console.log('descriptor',descriptor) descriptor.value = function () { console.log(
Calling “${name}” with`, arguments); return oldValue.apply(null, arguments); } return descriptor; }
function checkLogin (target, name, descriptor) { const isLogin = true const comp = descriptor.value // this指向的是构造函数 descriptor.value = function(){ return isLogin ? comp.apply(null, arguments) : ‘请先登录’ } } class Maths { @log add(a, b) { return a + b; } @checkLogin comp (value){ return “123”+ value } } const math = new Maths() math.add(2, 4)
<a name="uon2n"></a>
# 例子2
实现Midway的@Get、 @querystring、body装饰器
```typescript
import * as glob from 'glob';// 可以无限遍历目录
import * as Koa from 'koa';
import * as KoaRouter from 'koa-router';
import * as Parameter from 'parameter'
type HTTPMethod = 'get' | 'put' | 'del' | 'post' | 'patch'
type LoadOptions = {
extname?: string
}
type RouteOptions = {
prefix?: string;
middlewares?: Array<Koa.Middleware>
}
const router = new KoaRouter()
const decorate = (method: HTTPMethod, path: string, options: RouteOptions = {}, router: KoaRouter) => {
return (target, property: string) => {
process.nextTick(() => {
// 添加中间件数组
const middlewares = []
if (options.middlewares) {
middlewares.push(...options.middlewares)
}
console.log('target',target)
console.log('property',property)
//这里是类里面的方法。
console.log('target[property]',target[property])
if (target.middlewares) {
middlewares.push(...target.middlewares)
}
middlewares.push(target[property])
const url = options.prefix ? options.prefix + path : path
// router[method](url, target[property])
// router.get('/user/:id', controller.user.info);
router[method](url, ...middlewares)
})
}
}
const method = method => (path: string, options?: RouteOptions) => decorate(method, path, options, router)
export const get = method('get')
export const post = method('post')
export const put = method('put')
export const del = method('del')
// 相当于@controller。
export const load = (folder: string, options: LoadOptions = {}): KoaRouter => {
const extname = options.extname || '.{js,ts}'
glob.sync(require('path').join(folder, `./**/*${extname}`)).forEach((item) => require(item))
return router
}
// @middlewares
export const middlewares = (middlewares: Koa.Middleware[]) => {
return function (target) {
target.prototype.middlewares = middlewares
}
}
const validateRule = paramPart => rule => {
return function (target, name, descriptor) {
const oldValue = descriptor.value
descriptor.value = function () {
const ctx = arguments[0]
const p = new Parameter()
const data = ctx[paramPart]
const errors = p.validate(rule, data)
console.log('error',errors)
if (errors) throw new Error(JSON.stringify(errors))
return oldValue.apply(null, arguments);
}
return descriptor;
}
}
// @querystring
export const querystring = validateRule('query')
// @body
export const body = validateRule('body')