装饰器模式

什么是装饰器

原名decorator 被翻译为装饰器 可以理解为装饰 修饰 包装等意

现实中的作用

一间房子通过装饰可以变得更华丽,功能更多

类似一部手机可以单独使用 但是很多人都愿意家个保护套来防摔。。。

js中的作用

装饰器可以说是解决了不同类之间共享方法的问题(可以看做是弥补继承的不足)。

A Python decorator is a function that takes another function, extending the behavior of the latter function without explicitly modifying it.

这句话可以说是对装饰器的非常漂亮的解释。

在未来的 JavaScript 中也引入了这个概念,并且 babel 对他有很好的支持。如果你是一个疯狂的开发者,就可以借助 babel 大胆使用它。

环境准备

装饰器目前在浏览器或者 Node 中都暂时不支持,需要借助 babel 转化为可支持的版本

安装 babel

按照官网的 说明 安装:

npm install --save-dev babel-cli babel-preset-env

在 .babelrc 中写入:

{
"presets": ["env"]
}

按照说明,安装 babel-plugin-transform-decorators-legacy 插件:

npm install babel-plugin-transform-decorators-legacy --save-dev

.babelrc :

{
"presets": ["env"],
"plugins": ["transform-decorators-legacy"]
}

这样准备工作就完成了。

开始

先看看一个装饰器的写法

class Boy{
@run
speak (){
console.log('I can speak')
}
}
function run () {
console.log('I can run')
} let tj = new Boy()
tj.speak() // I can run
// I can speak

@run 就是给类属性方法(speak)加的一个装饰器(其实也就是一个函数) 扩展了类Boy的speak(在讲话的同时跑步)

装饰器不仅可以装饰类的方法还可以装饰类(但是不可以装饰函数,因为函数存在变量提升)

装饰器函数接受3个参数 分别是装饰的对象,装饰的属性,装饰属性的描述

class Boy{
@run
speak (){
console.log('I can speak')
}
}
function run (target,key,descripter) {
console.log(target,key,descripter)
} let tj = new Boy()
tj.speak()
// Boy {} 'speak' { value: [Function: speak],
writable: true,
enumerable: false,
configurable: true }
I can speak

再来看一个例子

class Math {
@log
add(a, b) {
return a + b
}
} function log(target, name, descriptor) {
var oldValue = descriptor.value descriptor.value = function() {
console.log(`Calling ${name} with`, arguments)
return oldValue.apply(target, arguments)
} return descriptor
} const math = new Math() // passed parameters should get logged now
math.add(2, 4)
// Calling add with { '0': 2, '1': 4 }

相当于在原来的add方法上扩展了一个console.log的功能,并没有改变原来的功能 (我们可以取到参数 并改变他)

还可以通过装饰器传递参数

function log(num) {
return function(target, name, descriptor) {
var oldValue = descriptor.value
let _num = num || 0
descriptor.value = (...arg) => {
arg[0] += _num
console.log(`Calling${target}, ${name} with`, arg)
return oldValue.apply(target, arg)
}
return descriptor
}
} class Math {
constructor(a = 3, b = 4) {
this.add(a, b)
}
@log(100)
add(a, b) {
return a + b
}
} const math = new Math() console.log(math)
console.log(math.add(9,1))

我们用装饰器来装饰koa-router

我们想给koa-router扩展更多的功能,并且是可读性维护性和代码的优雅性都很好的比如:

export default class MovieRouter{
@get('/api/v0/movie')
@auth()
@log()
...
}

让路由在真正处理业务的时候先做些其他的准备工作(如上先验证用户是否登录,然后输出日志)

就以上,我们先简单实现一下

const Koa = require('koa')
const app = new Koa()
const {connect} = require('../db/index')
const mongoose = require('mongoose')
const Shijue = mongoose.model('Shijue')
const Router = require('koa-router')
const router = new Router() // 连接数据库
void (async () => {
await connect()
})() class Route {
constructor() {
this.app = app
this.router = router
} init() {
routerMap.map(item=>{
router[item.method](item.path, item.callback)
})
app.use(router.routes())
app.use(router.allowedMethods())
}
} var routerMap = [] function get(path) {
return function(target, key, descriptor) {
routerMap.push({path, target, method: 'get', callback: target[key]})
return descriptor
}
}
var logTimes = 0
function log() {
return function(target, key, descriptor) {
app.use(async function(ctx, next) {
logTimes++
console.time(`${logTimes}: ${ctx.method} - ${ctx.url}`)
await next()
console.timeEnd(`${logTimes}: ${ctx.method} - ${ctx.url}`)
return descriptor
})
}
} class ShijueRouter {
@get('/api')
@log()
async getShijue(ctx, next) {
// await ...
return (ctx.body = {code: 0, data: 'shijue'})
}
} app.use(router.routes())
app.use(router.allowedMethods()) async function start() {
var r = new Route()
r.init()
app.listen(3001, function(err) {
if (err) {
console.log(err)
} else {
console.log('启动成功:3001')
}
})
}
start()

代码比较粗糙可以提炼分离

还有如react-redux的实现等

javascript装饰器模式的更多相关文章

  1. 从ES6重新认识JavaScript设计模式: 装饰器模式

    1 什么是装饰器模式 向一个现有的对象添加新的功能,同时又不改变其结构的设计模式被称为装饰器模式(Decorator Pattern),它是作为现有的类的一个包装(Wrapper). 可以将装饰器理解 ...

  2. JavaScript设计模式—装饰器模式

    装饰器模式介绍 为对象添加新的功能,不改变其原有的结构和功能,原有的功能还是可以使用,跟适配器模式不一样,适配器模式原有的已经不能使用了,装饰器示例比如手机壳 UML类图和代码示例 Circle示原来 ...

  3. Javascript 装饰器极速指南

    pablo.png Decorators 是ES7中添加的JavaScript新特性.熟悉Typescript的同学应该更早的接触到这个特性,TypeScript早些时候已经支持Decorators的 ...

  4. JS 设计模式九 -- 装饰器模式

    概念 装饰者(decorator)模式能够在不改变对象自身的基础上,动态的给某个对象添加额外的职责,不会影响原有接口的功能. 模拟传统面向对象语言的装饰者模式 //原始的飞机类 var Plane = ...

  5. 装饰器模式&&ES7 Decorator 装饰器

    装饰器模式(Decorator Pattern)允许向一个现有的对象动态添加新的功能,同时又不改变其结构.相比JavaScript中通过鸡肋的继承来给对象增加功能来说,装饰器模式相比生成子类更为灵活. ...

  6. PHP 装饰器模式

    装饰器模式:是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能. [装饰器模式中主要角色] 抽象组件角色(Component):定义一个对象接口,以规范准备接受附加责任的对象,即可以给这 ...

  7. 设计模式-装饰器模式(Decrator Model)

    文 / vincentzh 原文连接:http://www.cnblogs.com/vincentzh/p/6057666.html 目录 1.概述 2.目的 3.结构组成 4.实现 5.总结 1.概 ...

  8. php设计模式 装饰器模式

    装饰器模式,可以动态地添加修改类的功能. 一个类提供了一项功能,如果要修改并添加额外的功能,传统的编程模式需要写一个子类继承它,并重新实现类的方法.使用装饰器模式,仅需要在运行时添加一个装饰器对象即可 ...

  9. Java设计模式12:装饰器模式

    装饰器模式 装饰器模式又称为包装(Wrapper)模式.装饰器模式以多客户端透明的方式扩展对象的功能,是继承关系的一个替代方案. 装饰器模式的结构 通常给对象添加功能,要么直接修改对象添加相应的功能, ...

随机推荐

  1. emqtt 试用(七)追踪

    追踪 EMQ 消息服务器支持追踪来自某个客户端(Client)的全部报文,或者发布到某个主题(Topic)的全部消息. 追踪客户端(Client): ./bin/emqttd_ctl trace cl ...

  2. kubernetes入门(01)kubernetes是什么?

    一.kubernetes是什么? Kubernetes是Google开源的一个容器编排引擎,它支持自动化部署.大规模可伸缩.应用容器化管理.在生产环境中部署一个应用程序时,通常要部署该应用的多个实例以 ...

  3. angular2 学习笔记 ( unit test 单元测试 )

    第一次写单元测试. 以前一直都有听说 TDD 的事情. 今天总算是去尝试了一下. 先说说 TDD 的想法, 是这样的, 开发项目的流程 : 确定需求 -> 写类,接口,方法的名字(不写具体实现代 ...

  4. Linux探索之路1---CentOS入坑笔记整理

    前言 上次跟运维去行方安装行内环境,发现linux命令还是不是很熟练.特别是用户权限分配以及vi下的快捷操作.于是决定在本地安装一个CentOS虚拟机,后面有时间就每天学习一点Linux常用命令. 作 ...

  5. python--socket粘包

    socket粘包 1 什么是粘包 须知:只有TCP有粘包现象,UDP永远不会粘包,首先需要掌握一个socket收发消息的原理, 所谓粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少 ...

  6. python——常用模块

    python--常用模块 1 什么是模块: 模块就是py文件 2 import time #导入时间模块 在Python中,通常有这三种方式来表示时间:时间戳.元组(struct_time).格式化的 ...

  7. jacascript 函数声明、函数表达式与声明提升(hoisting机制)

    前言:这是笔者学习之后自己的理解与整理.如果有错误或者疑问的地方,请大家指正,我会持续更新! 声明.定义.初始化 声明的意思是宣称一个变量名的存在,定义则为这个变量分配存储空间,初始化则是给该变量名的 ...

  8. 部署testlink报错,安装wampserver时提示丢失MSVCR110.dll

    安装wampserver时提示丢失MSVCR110.dll(在windows server上可用)对于32位系统,安装Wampserver 后启动的时候提示系统错误:MSVCR110.dll丢失.于是 ...

  9. SQL将一个数据库中的数据复制到另一个数据库中

    复制表结构 首先,打开并连接Sql Server,在源数据库Source_db(源数据库名称)上右键,然后依次点击"编写表脚本为"→"CREATE到"→&quo ...

  10. vue 2.0 路由切换以及组件缓存源代码重点难点分析

    摘要 关于vue 2.0源代码分析,已经有不少文档分析功能代码段比如watcher,history,vnode等,但没有一个是分析重点难点的,没有一个是分析大命题的,比如执行router.push之后 ...