1、通用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Function.prototype.before = function( beforefn ) {
const _self = this;
return () => {
beforefn.apply( this, arguments )
return _self.apply( this, arguments )
}
}

Function.prototype.after = function( afterfn ) {
const _self = this;
return () => {
const ret = _self.apply( this, arguments )
afterfn.apply( this, arguments )
return ret
}
}

不污染写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const before = function ( fn, beforefn ) {
return function(){
beforefn.apply( this, arguments )
return fn.apply( this, arguments )
}
}

const a = before(
function(){alert(3)},
function(){alert(2)}
)

a = before(a, function(){alert(1)})
a()

2、例子:数据统计上报

1
2
3
4
5
6
7
8
9
10
11
const showLogin = () => {
console.log( '打开登录浮层' )
}

const log = () => {
console.log( `上报标签为:${this.getAttribute( 'tag' )}` )
}

showLogin = showLogin.after( log )

document.getElementById( 'button' ).onclick = shoLogin

3、例子:用 AOP 动态改变函数的参数

1
2
3
4
5
6
7
8
9
const func = param => {
console.log( param )
}

func = func.before(param => {
param.b = 'b'
})

func( {a: 'a'} ) // 输出:{a: 'a', b: 'b'}

4、例子:插件式的表单验证

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
31
32
33
34
35
Function.prototype.before = function( beforefn ) {
const _self = this;
return () => {
if ( beforefn.apply( this, arguments ) === false ) {
// beforefn 返回 false 的情况直接 return , 不再执行后面的原函数
return
}
return _self.apply( this, arguments )
}
}

const validata = () => {
if ( username.value === '' ) {
alert ( '用户名不能为空' )
return false
}
if ( password.value === '' ) {
alert ( '密码不能为空' )
return false
}
}

const formSubmit = () => {
const param = {
username: username.value,
password: password.value
}
ajax( 'http://xxx.com/login', param )
}

formSubmit = formSubmit.before( validata )

submitBtn.onclick = () => {
fromSubmit()
}

5、注意

被装饰过的函数,返回的实际上是一个新的函数,如果在原函数上保存了一些属性,那么这些属性会丢失。

1
2
3
4
5
6
7
8
9
10
const func = () => {
alert( 1 )
}
func.a = 'a'

func = func.after(()=>{
alert( 2 )
})

alert( func.a ) // 输出:undefined

6、装饰者模式与代理模式

最重要的区别是意图和设计目的。

在虚拟代理实现预图片加载的例子中,本体负责设置 img 节点的 src,代理则提供了预加载的功能,这看起来也是“加入行为”的一种方式,但这种方式和装饰者模式的偏重点不一样。装饰者模式是实实在在的为对象增加新的职责和行为,而代理模式的事情还是跟本体一样,最终都是设置src。但代理加入了一些“聪明”的功能,比如在图片真正加载好之前,先使用一张占位的loading图片反馈给客户。