主题
装饰器模式
环境
浏览器和 Node 目前都不支持装饰器语法,需要大家安装 Babel 进行转码
shell
npm i -D babel-preset-env babel-plugin-transform-decorators-legacy
编写 .babelrc
文件
json
{
"presets": ["env"],
"plugins": ["transform-decorators-legacy"]
}
本地运行
shell
npm i babel-cli -D
json
{
"scripts": {
"start": "babel-node xxx.js"
}
}
概念
在不改变原对象的基础上,通过对其进行包装拓展,使原有对象可以满足用户的更复杂需求
OOP -- 面向对象编程模式
给对象添加功能时,通常会采用继承的方式,但继承的方式也有许多问题
父类和子类存在强耦合关系,父类改变时,子类同时也需要改变。同时子类需要知道父类的接口等
js 只能单继承,当需要对多个对象添加功能时,特别麻烦
AOP -- 面向切面编程
把功能逻辑抽离出来,在通过动态切入的方式注入到业务逻辑中,是可以保证业务内部的高内聚和业务模块之间的低耦合。
装饰和 AOP 本质是一样的
日志上报例子
业务想知道一个业务方法,执行前的时间,与执行后的时间。但这个业务方法不是你写的,所以你并不想去动别人的代码。这个时候就可以用 java spring 实现的前置通知和后置通知的思路
ES5
js
// 前置通知
Function.prototype.before = function (beforefn) {
const __self = this
return function () {
beforefn.apply(this, arguments)
return __self.apply(this, arguments)
}
}
js
// 后置通知
Function.prototype.after = function (afterfn) {
const __self = this
return function () {
const ret = __self.apply(this, arguments)
afterfn.apply(this, arguments)
return ret
}
}
这样就可以很快的完成新的业务功能了
js
function Person(name) {
this.name = name
}
Person.prototype.say = function () {
console.log(`my name is ${this.name}`)
}
// 通知函数
function log() {
console.log(`person say on ${new Date()}`)
}
Person.prototype.say = Person.prototype.say.before(log).after(log)
const musi = new Person('musi')
musi.say()
// person say on Sun Nov 06 2022 21:32:32 GMT+0800 (中国标准时间)
// my name is musi
// person say on Sun Nov 06 2022 21:32:32 GMT+0800 (中国标准时间)
ES7
js
function before(beforefn) {
return function (target, name, descriptor) {
const value = descriptor.value
descriptor.value = function (...arg) {
beforefn.apply(this, arg)
return value.apply(this, arg)
}
return descriptor
}
}
function after(afterfn) {
return function (target, name, descriptor) {
const value = descriptor.value
descriptor.value = function (...arg) {
const ret = value.apply(this, arg)
afterfn.apply(this, arg)
return ret
}
return descriptor
}
}
js
// 通知函数
function log() {
console.log(`person say on ${new Date()}`)
}
class Person {
constructor(name) {
this.name = name
}
@before(log)
@after(log)
say() {
console.log(`my name is ${this.name}`)
}
}
const musi = new Person('musi')
musi.say()
装饰器使用
类装饰器
js
function classDecorator(target) {
target.hasDecorator = true
return target
}
// 将装饰器“安装”到Button类上
@classDecorator
class A {
// Button类的相关逻辑
}
方法装饰器
js
function funcDecorator(target, name, descriptor) {
const originValue = descriptor.value
descriptor.value = function() {
// ...
return originValue.apply(this, arguments)
}
return descriptor
}
class A {
@funcDecorator
fn() {
}
}
带参数的方法装饰器
js
function funcDecorator(...arg) {
return function (target, name, descriptor) {
// 用 arg 做些什么
const originValue = descriptor.value
descriptor.value = function () {
// ...
return originValue.apply(this, arguments)
}
return descriptor
}
}
descriptor 和
Object**.**defineProperty(obj, prop, descriptor)
里的是一个东西。专门用来描述对象的属性