javascript 设计模式之单例模式javascript
javascript设计模式之代理模式github
装饰者(decorator)模式,又名装饰器模式,可以在不改变对象自身的基础上,在程序运行期间给对像动态的添加职责。与继承相比,装饰者是一种更轻便灵活的作法。
就比如手机扣,有了手机扣会方便观看视频,但对于手机原先的全部功能,像是拍照仍然能够直接使用。手机扣只是起到锦上添花做用,并不必定要有。
初始需求:
画个圆
实现:
class Circle {
draw() {
console.info('画圆')
}
}
let c = new Circle()
c.draw()
复制代码
更改需求:
画个红色的圆
实现:
或许这时你二话不说,就是找到 Circle 的 draw 方法改为以下:
class Circle {
draw() {
console.info('画圆')
this.setRed()
}
setRed() {
console.info('设置红色边框')
}
}
let c = new Circle()
c.draw()
复制代码
若是需求不改,这种实现方式倒也没问题。 但若是哪天经理说我要实现个绿色边框,是否是又得改为:
class Circle {
draw() {
console.info('画圆')
this.setGreen()
}
setRed() {
console.info('设置红色边框')
}
setGreen() {
console.info('设置绿色边框')
}
}
let c = new Circle()
c.draw()
复制代码
这种方式存在两个问题:
为了新的业务需求不影响到原有功能,须要将旧逻辑与新逻辑分离:
class Circle {
draw() {
console.info('画圆')
}
}
class Decorator {
constructor(circle) {
this.circle = circle
}
draw() {
this.circle.draw()
this.setRedBorder()
}
setRedBorder() {
console.info('设置红色边框')
}
}
let c = new Circle()
let d = new Decorator(c)
d.draw()
复制代码
此时若是要画别的颜色圆形,只要修改 Decorator 类便可。
如此一来,就实现了"只添加,不修改原有的类"的装饰者模式,使用 Decorator 的逻辑装饰了旧的圆形逻辑。 固然你仍然能够单纯只建立个不带颜色的圆形,用 c.draw() 便可
AOP(Aspect Oriented Programming)面向切面编程。把一些与核心业务逻辑无关的功能抽离出来,再经过“动态织入”方式掺入业务逻辑模块
与业务逻辑无关的功能一般包括日志统计、安全控制、异常处理等等。
首先咱们要实现两个函数:
一个用来前置装饰,一个用来后置装饰:
Function.prototype.before = function(beforeFunc){
var that = this;
return function(){
beforeFunc.apply(this, arguments);
return that.apply(this, arguments);
}
}
Function.prototype.after = function(afterFunc){
var that = this;
return function(){
var ret = that.apply(this, arguments);
afterFunc.apply(this, arguments);
return ret;
}
}
复制代码
之前置装饰 before 为例,调用 before 时,传入一个函数,这个函数即为新添加的函数,它装载了新添加的功能代码。
接着把当前的 this 保存起来,而后返回一个“代理”函数。这样在原函数调用前,先执行扩展功能的函数,并且他们共用同一个参数列表。
后置装饰与前置装饰基本相似,只是执行顺序不一样
验证:
var foobar = function (x, y, z) {
console.log(x, y, z)
}
var foo = function (x, y, z) {
console.log(x / 10, y / 10, z / 10)
}
var bar = function (x, y, z) {
console.log(x * 10, y * 10, z * 10)
}
foobar = foobar.before(foo).after(bar)
foobar(1, 2, 3)
复制代码
输出:
0.1 0.2 0.3
1 2 3
10 20 30
复制代码
以上设置 before 与 after 的方法污染了原型,能够改为:
var before = function (fn, beforeFunc) {
return function () {
beforeFunc.apply(this, arguments)
return fn.apply(this, arguments)
}
}
var after = function (fn, afterFunc) {
return function () {
var ret = fn.apply(this, arguments)
afterFunc.apply(this, arguments)
return ret
}
}
var a = before(
function () {
alert(3)
},
function () {
alert(2)
}
)
a = before(a, function () {
alert(1)
})
a()
复制代码
输出:
1
2
3
复制代码
{
"presets":["es2015","latest"],
"plugins": ["transform-decorators-legacy"] // 加上插件支持 ES7 的装饰语法
}
复制代码
// 装饰器函数,它的第一个参数是目标类
function classDecorator(target) {
target.hasAdd = true
// return target // 无关紧要, 默认就是返回 this 的
}
// 将装饰器"安装"到 Button 类上
@classDecorator
class Button {}
// 验证装饰器是否生效
alert('Button 是否被装饰了:' + Button.hasAdd)
复制代码
等价于
function classDecorator(target) {
target.hasAdd = true
return target // 此时必定要用, 由于这时是做为函数使用,而非构造函数
}
class Button {}
Button = classDecorator(Button)
// 验证装饰器是否生效
alert('Button 是否被装饰了:' + Button.hasAdd)
复制代码
说明装饰器的原理:
@decorator
class A{}
//等同于
A = decorator(A) || A
复制代码
代表 ES7 中的装饰器也是个语法糖
// 装饰器要接收参数时,就要返回个函数,该函数的第一个参数是目标类
function classDecorator(name) {
return function (target) {
target.btnName = name
}
}
// 将装饰器"安装"到 Button 类上
@classDecorator('登陆')
class Button {}
// 验证装饰器是否生效
alert('按钮名称:' + Button.btnName)
复制代码
等同于
// 装饰器要接收参数时,就要返回个函数,该函数的第一个参数是目标类
function classDecorator(name) {
return function (target) {
target.btnName = name
return target
}
}
// 将装饰器"安装"到 Button 类上
class Button {}
Button = classDecorator('登陆')(Button)
// 验证装饰器是否生效
alert('按钮名称:' + Button.btnName)
复制代码
function mixin(...list) {
console.info(...list, 'list')
// ...list 是个对象, key 为 "foo",值为 function() { alert('foo')}
return function (target) {
Object.assign(target.prototype, ...list)
console.dir(target, 'target')
}
}
const Foo = {
foo() {
alert('foo')
}
}
@mixin(Foo)
class Button {}
let d = new Button()
d.foo()
复制代码
能够看到是往 Button 类的原型上加上了 foo 函数,那可能有人会问了,为何不在类上直接加呢,即
function mixin(...list) {
console.info(...list, 'list') // ...list 是个对象, key 为 "foo",值为 function() { alert('foo')}
return function (target) {
Object.assign(target, ...list)
console.dir(target, 'target')
}
}
const Foo = {
foo() {
alert('foo')
}
}
@mixin(Foo)
class Button {}
let d = new Button()
d.foo()
复制代码
此时会报Uncaught TypeError: d.foo is not a function
错误
这是因为实例是在代码运行时动态生成的,而装饰器函数则是在编译阶段就执行了,因此装饰器 mixin 函数执行时, Button 实例还不存在。
为了确保实例生成后能够顺利访问到被装饰好的方法(foo),装饰器只能去修饰 Button 类的原型对象。
function readonly(target, name, descriptor) {
descriptor.writable = false
return descriptor
}
class Person {
constructor() {
this.first = 'A'
this.last = 'B'
}
@readonly
name() {
return `${this.first} ${this.last}`
}
}
let p = new Person()
console.info(p.name())
// p.name = function () {
// console.info(100)
// } // 修改会报错
复制代码
function log(target, name, descriptor) {
let oldValue = descriptor.value
descriptor.value = function () {
console.log(`calling ${name} width`, arguments)
return oldValue.apply(this, arguments)
}
return descriptor
}
class Math {
@log
add(a, b) {
return a + b
}
}
let math = new Math()
const result = math.add(4, 6)
alert(result)
复制代码
以上 readonly 与 log 都是经过修改 descriptor 实现的,那该装饰方法的函数的三个参数都分别表示什么呢?
Object.defineProperty(obj, prop, descriptor)
里的 descriptor目前有个开源的第三方库 core-decorators,提供了不少好用的装饰方法。
// import { readonly } from 'core-decorators'
// class Person {
// @readonly
// name() {
// return 'zhang'
// }
// }
// let p = new Person()
// alert(p.name())
// // p.name = function () { /*...*/ } // 此处会报错
import { deprecate } from 'core-decorators'
class Person {
@deprecate
facepalm() {}
@deprecate('We stopped facepalming')
facepalmHard() {}
@deprecate('We stopped facepalming', {
url: 'http://knowyourmeme.com/memes/facepalm'
})
facepalmHarder() {}
}
let person = new Person()
person.facepalm()
// DEPRECATION Person#facepalm: This function will be removed in future versions.
person.facepalmHard()
// DEPRECATION Person#facepalmHard: We stopped facepalming
person.facepalmHarder()
// DEPRECATION Person#facepalmHarder: We stopped facepalming
//
// See http://knowyourmeme.com/memes/facepalm for more details.
复制代码
你的点赞是对我最大的确定,若是以为有帮助,请留下你的赞扬,谢谢!!!