能動態的給對象增加職責的方式稱爲裝飾者模式。
裝飾者模式能夠在不改變對象自身的基礎上,在程序運行期間給對象動態地添加職責。跟繼承相比,裝飾者模式靈活很多,“即用即付”,有點熱插拔那味了。
1,裝飾者模式實現飛機大戰
現在我們有一個飛機,初始1級的時候可以發射子彈,2級之後可以發射導彈,3級的時候可以發射原子彈:
//一個簡單地打飛機
var plane = {
fire: function () {
console.log('發射普通子彈')
}
}
var missleDecorator = function () {
console.log('發射導彈')
}
var atomDecorator = function () {
console.log('發射原子彈')
}
var fire1 = plane.fire
plane.fire = function () {
fire1()
missleDecorator()
}
var fire2 = plane.fire
plane.fire = function () {
fire2()
atomDecorator()
}
plane.fire()
// 分別輸出: 發射普通子彈 發射導彈 發射原子彈
2,用AOP裝飾函數
使用AOP也就是要在函數執行的before和after做一些處理,那麼我們就實現一下Function.prototype.before和Function.prototype.after:
//AOP裝飾函數
Function.prototype.before = function (beforeFn) {
var _self = this
//返回原函數和新函數的’代理‘函數
return function () {
//保證this不被劫持
beforeFn.apply(this, arguments)
return _self.apply(this, arguments)
}
}
Function.prototype.after = function (afterFn) {
var _self = this
return function () {
var result = _self.apply(this, arguments)
afterFn.apply(this, arguments)
return result
}
}
可以在下面的例子中體會_self的用處
3,試一試before的威力
Function.prototype.before = function (beforeFn) {
var _self = this
//返回原函數和新函數的’代理‘函數
return function () {
//保證this不被劫持
beforeFn.apply(this, arguments)
return _self.apply(this, arguments)
}
}
在這個例子中,_self指的是document.getElementById.before這個函數上面,bofore的調用者,實際上是getElementById而不是document,因爲getElementById是最後一層的調用者,當document.getElementById = document.getElementById.before()之後,document.getElementById指向的就是before返回的函數,其中的this就是getElementById函數的調用者,也就是document。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<div id="button">11</div>
</head>
<body>
<script>
/**
* 裝飾者模式和代理模式的區別
* 裝飾者可以添加很多不同的東西,有點按需加載的意思
* 代理只是專門處理一件事,需求一開始就是明確的
*/
Function.prototype.before = function (beforeFn) {
var _self = this
//返回原函數和新函數的’代理‘函數
return function () {
//保證this不被劫持
beforeFn.apply(this, arguments)
return _self.apply(this, arguments)
}
}
Function.prototype.after = function (afterFn) {
var _self = this
return function () {
var result = _self.apply(this, arguments)
afterFn.apply(this, arguments)
return result
}
}
document.getElementById = document.getElementById.before(function () {
console.log('before')
})
var button = document.getElementById('button')
window.alert = window.alert.before(function () {
console.log(1)
})
alert(2)
</script>
</body>
</html>