向一个对象添加新的功能, 同时又不改变其结构
创建一个装饰类, 用来包装原有的类, 提供额外的功能, 相比生成子类来说更为灵活
应用场景
新入职的同事接手一个老项目, 对业务不熟, 并且老项目封装好的方法不让动(改), 这时候我们就可以拓展一个方法,并且不影响封装好的函数下完成功能
例子
比如项目封装了一个上传文件的函数, 现在新增需求 “每次上传前判断下用户角色”
import axios from 'axios'
class Upload {
constructor(url, file) {
this.url = url
this.file = file
}
http() {
axios.post(this.url, this.file)
}
}
上面这个是老项目封装好的方法, 接下来我们拓展下
class Decorator {
constructor(upload, user) {
this.upload = upload
this.user = user
}
http() {
//新增判断用户权限
if (this.user.code < 1) {
alert("您没权限上传文件")
} else {
this.upload.http()
}
}
}
接下来就是调用了
const upload = new Upload('http://www.xx.com', '1.png')
//修饰后的上传方法
const decorateUpload = new Decorator(upload, {code: 3})
decorateUpload.http() //正常发送请求
这时候换了个用户code 为 -1
const otherUser = new Decorator(upload, {code: -1})
decorateUpload.http()
//此时请求就被拒绝了, 提示无权限
ES7 装饰器
//修饰器函数 (第一个参数,就是所要修饰的目标类)
function myLog(target) {
target.log = function (msg) {
console.log(msg)
}
}
@myLog class Test {
...
}
Test.log('我是一个小日志') //这里就正常打印出msg
修饰器不但可以修饰类,还可以修饰类的属性
function readOnly(target, key, descriptor) {
console.log(target, key, descriptor)
descriptor.writable = false; //其实就是Object.defineProperty(target, key, {writable: false })
return descriptor;
}
class Person {
@readOnly static name= '装饰器模式'
@readOnly like() {
console.log('我是不可修改的方法')
}
}
接下来尝试下修改类属性
console.log(Person.name) //装饰器模式
Person.name = '变变变' //TypeError: Cannot assign to read only property 'name' of ...
图1:
const p = new Person()
p.like = {} //TypeError: Cannot assign to read only property 'like' of object '#<Person>'
图2:
可以看到两个只读起作用了, 并且还可以同时用多个修饰器
function readOnly() {...}
function log() {...}
class Person {
@log
@readOnly
like() {
console.log('我是不可修改的方法, 并且会打印日志')
}
}
划重点: 当存在多个修饰器时, 就跟洋葱模型一样, 先从外到内进入 再从 内到外执行
看下面这个例子输出顺序就知道了
function add(num) {
console.log('进入装饰器', num)
return target => {
console.log('执行装饰器', target, num++)
}
}
class Person {
@add(1)
@add(2)
age = 10
}
new Person()
图3:
但是这个es7修饰器不能用于普通函数,只能修饰 类
, 日常开发也不会全部自己写这么多修饰方法, core-decorators.js
它提供了几个常见的修饰器
最后的轻语
在书里看到一句话,不是很理解:
在现实世界中思考理论问题, 在理论世界中思考现实问题