在程序开发时,我们不希望某个类或者函数的方法非常复杂,一次就包含很多职责;那么我们可以采用装饰者模式,它可以在不改变原生对象的基础上,动态地给某个对象添加一些额外的方法或者属性,使其满足更复杂的用户需求。
实现
一个例子,我们希望在原有的onload基础上添加一个方法,而我们不想改变原有的onload方法(或者方法太复杂,总之我们保持器神秘性),那么我们可以这么做:
window.onload = funciton(){// 不知道的逻辑 ***}
function my(){alert(2);} // 我自己的逻辑
var _onload = window.onload || function(){};
window.onload = funciton(){
_onload();
my();
}
上面这样的做法有可能会产生一个问题,那就是作用域的不延续(也就是this被劫持的问题),比如:
var _getElementById = document.getElementById;
document.getElementById = function(id){
alert(1);
return _getElementById( id ); // 调用的时候会抛出异常,因为getElementById需要document作用域。
}
为了解决这类问题,同时提出一个比较适用的装饰者解决方案,我们借组AOP进行实现:
// 实现1 本文都采用此方法
Function.prototype.before = function( fn ){
var _self = this;
return function(){
fn.apply(this, arguments);
_self.apply(this, arguments);
}
}
Function.prototype.after = function( fn ){
var _self = this;
return function(){
_self.apply(this, arguments);
fn.apply(this, arguments);
}
}
// 实现2
Function.prototype._$aop = function(_before,_after){
var f = function(){},
_after = _after||f,
_before = _before||f,
_handler = this;
return function(){
var _event = {args:[].slice.call(arguments,0)};
_before(_event);
if (!_event.stopped){
_event.value = _handler
.apply(this,_event.args);
_after(_event);
}
return _event.value;
};
};
那么可以把上面的方法进行如下装饰:
document.getElementById = document.getElementById.before(function(){
alert(1);
})
实用
几个用到AOP的地方:
-
数据统计: 如果我们要统计事件,假设点击出现登录框时要发送统计请求,可以采用如下所示的方法:
var showLogin = function(){ // open the dialog } var log = function(){ // 上报 } showLogin = showLogin.after(log);
-
改变函数参数: 当我们网站受到攻击时,需要在ajax请求中加上一个token参数,如下:
// 原来的ajax var ajax = funciton( type, url, param ){// ajax逻辑} // 修改后的 var ajax = function( type, url, param ){ param = param || {}; param.token = getToken(); ajax(type, url, param); // ajax逻辑 } // 如果我们又不想改变原来的ajax库(有可能以后新的项目需要用到,互联网总是很多的新项目..) ajax = ajax.before(function(type, url, param){ param.token = getToken(); });
-
校验需求: 提交表单时,校验不通过,直接返回。
Function.prototype.before = function( fn ){ var _self = this; return function(){ if (fn.apply(this, arguments) === false){ return; } _self.apply(this, arguments); } } function formSumbit(){ // ajax提交 } function validate(){ if( user.name === ''){ alert("名字为空"); return false; } ... } formSumbit = formSumbit.before(validate);
装饰者模式和代理模式的区别
代理模式的目的是,当直接访问本体不方便或者不符合需求时,为这个本体提供一个代替者。本体定义了关键功能,而代理提供或拒绝对它的访问,或者在访问本体前做一些额外的事情。 换句话说,代理模式强调一种关系(Proxy与它的实体之间的关系),这种关系一开始就可以被确定。以图片加载为例,为图片设置src时最终目的,而在之前设置一个loading图片是一个聪明的做法。
装饰者模式是为对象动态的加入行为,用于不能确定本体对象全部功能的情况下,因此有可能形成一条长长的装饰链。
网友评论