视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
JavaScript装饰者模式
2020-11-27 20:26:19 责编:小采
文档


在传统的面向对象语言中,给对象添加功能常常使用继承的方式,但继承的方式会带来问题:当父类改变时,他的所有子类都将随之改变。

当JavaScript脚本运行时,在一个对象中(或他的原型上)增加行为会影响该对象的所有实例,

装饰者是一种实现继承的替代方案,它通过重载方法的形式添加新功能,该模式可以在被装饰者前面(before)或者后面(after)加上自己的行为以达到特定的目的。

装饰者模式是为已有功能动态地添加更多功能的一种方式,把每个要装饰的功能放在单独的函数里,然后用该函数包装所要装饰的已有函数对象,因此,当需要执行特殊行为的时候,调用代码就可以根据需要有选择地、按顺序地使用装饰功能来包装对象。优点是把类(函数)的核心职责和装饰功能区分开了。

我们可以定义工具函数,如下:

Function.prototype.before = function (beforeFn) {
 var self = this; //保存原函数的引用
 return function () { //返回包含了新函数和原函数的代理函数
 beforeFn.apply(this,arguments); //执行新函数,且保证this不被劫持
 return self.apply(this,arguments); //执行原函数,并返回原函数的执行结果,并保证this不被劫持
 }
};
Function.prototype.after = function (afterFn) {
 var self = this;
 return function () {
 var ret = self.apply(this,arguments);
 afterFn.apply(this,arguments);
 return ret;
 }
};

这里的参数beforeFn、afterFn即为要为原函数扩展新功能的新函数(添加装饰),它们的唯一区别是执行顺序的不同。如果不想污染Function的原型,可以用下面的方法:

var before = function (fn, beforeFn) {
 return function () {
 beforeFn.apply(this,arguments);
 return fn.apply(this,arguments);
 }
};
var after = function (fn, afterFn) {
 return function () {
 var ret = fn.apply(this,arguments);
 afterFn.apply(this,arguments);
 return ret;
 }
};

例子:给HTTP请求中带上一个参数防止CSRF攻击

var ajax = function (type, url, param) {
 console.log(param); //发送ajax请求代码略...
};
var beforeFn = function (type, url, param) {
 param.Token = 'Token';
};
ajax = ajax.before(beforeFn);
ajax('get','http://...com/userinfo',{name:'SuFa'});
//{ name: 'SuFa', Token: 'Token' }

通过给ajax函数动态装饰上Token参数,而不是直接在原函数上修改参数,保证了ajax函数仍然是一个纯净的函数,提高了它的可复用性,它可在无需做任何修改的情况下直接拿到别的项目中使用。

例子:表单验证(把验证输入和表单提交的代码分离开来,然后动态的把验证输入功能装饰到表单提交之前,这样一来,我们就可以把验证输入部分写成一个插件的形式,用在不同的项目中)

//验证输入函数
var validata = function () {
 if(username.value === ''){
 alert('用户名不能为空');
 return false;
 }
 if(password.value === ''){
 alert('密码不能为空');
 return false;
 }
};
//表单提交函数
var formSubmit = function () {
 var param = {
 username: username.value,
 password: password.value
 };
 ajax('http://xxx.com/login',param);
};
 
formSubmit = formSubmit.before(validata);
submitBtn.onclick = function(){
 formSubmit();
};

下载本文
显示全文
专题