概念介绍
应用对象:类。
装饰器模式(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@gx
以上的写法相当于
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装饰器```typescriptimport * 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}// @middlewaresexport const middlewares = (middlewares: Koa.Middleware[]) => {return function (target) {target.prototype.middlewares = middlewares}}const validateRule = paramPart => rule => {return function (target, name, descriptor) {const oldValue = descriptor.valuedescriptor.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;}}// @querystringexport const querystring = validateRule('query')// @bodyexport const body = validateRule('body')
