0%

装饰器模式

装饰器模式是在对原有功能的基础上对功能拓展的模式,能够在不改变对象自身的基础上,在程序运行期间给对象动态增添职责

为输入框增加新功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//装饰者
var decorator=function(input,fn){
//获取事件源
var input=document.getElementById(input)
//若事件源已经绑定事件
if(typeof input.onclick==='function'){
//缓存事件原有回调函数
var oldClickFn=input.onclick
input.onclick=function(){
//事件原有回调函数
oldClickFn()
//执行事件新增回调函数
fn()
}
}else{
//事件源未绑定事件,直接为事件源添加新增回调函数
input.onclick=fn
}
}
//电话输入框功能装饰
decorator('tel_input',function(){
document.getElementById('tel_demo_text').style.display='none'
})
//姓名输入框功能装饰
decorator('name_input',function(){
document.getElementById('name_demo_text').style.display='none'
})

装饰链:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
let plane ={
fire:function(){
console.log('发射普通子弹')
}
}
let missileDecorator = function () {
console.log('发射导弹')
}
let atomDecorator = function(){
console.log('发射原子弹')
}
let fire1 = plane.fire
plane.fire=function(){
fire1()
missileDecorator()
}
let fire2 = plane.fire
plane.fire = function(){
fire2()
atomDecorator()
}

plane.fire()//发射普通子弹 发射导弹 射原子弹

AOP装饰函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Function.prototype.before = function(beforefn){
let _self = this//保存原函数的引用
return function(){//返回包含了原函数和新函数的代理函数
beforefn.apply(this,arguments)//执行新函数,且保证this不被劫持,新函数接受的参数也会被原封不动地传入原函数,新函数在原函数之前执行
return _self.apply(this,arguments)//执行原函数并返回原函数的执行结果,并且保证this不被劫持
}
}
Function.prototype.after = function(afterfn){
let _self = this
return function(){
let ret = _self.apply(this,arguments)
afterfn.apply(this,arguments)
return ret
}
}
document.getElementById= document.getElementById.before(function(){
alert(1)
})
let button = document.getElementById('button')

应用:

数据统计上报:

把行为依照职责分成细粒度更细的函数,通过装饰把它们合并到一起,有助于编写一个松耦合高复用得到系统

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Function.prototype.after = function(afterfn){
let _self = this
return function(){
let ret = _self.apply(this,arguments)
afterfn.apply(this,arguments)
return ret
}
}
let showLogin = function(){
console.log('打开登录浮层')
}
let log = function(){
console.log('上报标签为:'+this.getAttribute('tag'))
}
showLogin = showLogin.after(log)//打开浮层后上报数据
document.getElementById('button').onclick = showLogin

动态改变参数

1
2
3
4
5
6
7
8
9
10
11
//动态改变参数
let ajax = function(type,url,param){
console.log(param)
}
let getToken = function(){
return "Token"
}
ajax = ajax.before(function(type,url,param){
param.Token = getToken()
})
ajax('get','http://xxx.com',{name:'seven'})

用AOP的方式给ajax函数动态装饰上Token参数,保证了ajax函数是一个相对纯净的函数,提高了ajax函数的复用性。

表单验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//表单验证
let validate = function(){
if(username.value === ''){
alert('用户名不能为空')
return false
}
if(password.value === ''){
alert('密码不为空')
return false
}
}
Function.prototype.before = function(beforefn){
let _self = this
return function(){
if(beforefn.apply(this,arguments)===false){
return ;
}
return _self.apply(this,arguments)
}
}
let formSubmit = function(){
let param ={
username:username.value,
password:password.value
}
ajax('http://xxx.com/login',param)
}
formSubmit=formSubmit.before(validate)
let submit = document.getElementById('submitBtn')
submit.onclick=function () {formSubmit()}

代理模式和装饰者模式区别:

目的不同:代理模式的目的是当直接访问本体不方便或者不符合需要的时候,为这个本体提供一个替代者,本体定义关键功能,代理提供或拒绝对它的访问,这种关系一开始就可以被确定。装饰者模式用于一开始无法确定对象的全部功能。

形式:代理模式只有一层代理-本体的引用,而装饰者模式是会形成一条长长的装饰链