学习JavaScript设计模式之装饰者模式


Posted in Javascript onJanuary 19, 2016

有时我们不希望某个类天生就非常庞大,一次性包含许多职责。那么我们就可以使用装饰着模式。
装饰着模式可以动态地给某个对象添加一些额外的职责,从而不影响这个类中派生的其他对象。
装饰着模式将一个对象嵌入另一个对象之中,实际上相当于这个对象被另一个对象包装起来,形成一条包装链。

一、不改动原函数的情况下,给该函数添加些额外的功能

1. 保存原引用

window.onload = function() {
  console.log(1);
};

var _onload = window.onload || function() {};

window.onload = function() {
  _onload();
  console.log(2);
}

问题:
(1)必须维护中间变量
(2)可能遇到this被劫持问题
在window.onload的例子中没有这个烦恼,是因为调用普通函数_onload时,this也指向window,跟调用window.onload时一样。

2. this被劫持:

var _getElementById = document.getElementById;
document.getElementById = function(id) {
  console.log(1);
  return _getElementById(id);
}

return _getElementById(id); // 报错“Uncaught TypeError: Illegal invocation”

因为_getElementById是全局函数,当调用全局函数时,this是指向window的,而document.getElementById中this预期指向document。

3. 解决this被劫持:

var _getElementById = document.getElementById;
document.getElementById = function(id) {
  console.log(1);
  return _getElementById.call(document, id);
}

二、用AOP装饰函数

/* 让新添加的函数在原函数之前执行(前置装饰)*/
Function.prototype.before = function(beforefn) {
  var _self = this;
  return function() {
    beforefn.apply(this, arguments);  // 新函数接收的参数会被原封不动的传入原函数
    return _self.apply(this, arguments);
  };
};
/* 让新添加的函数在原函数之后执行(后置装饰)*/
Function.prototype.after = function(afterfn) {
  var _self = this;
  return function() {
    var ret = _self.apply(this, arguments);
    afterfn.apply(this, arguments);
    return ret;
  };
};
document.getElementById = document.getElementById.before(function() {
  console.log(1);
});

三、避免污染原型

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;
  };
};

document.getElementById = before(document.getElementById, function(){
  console.log(1);
});

四、示例?插件式的表单验证

结合《学习JavaScript设计模式之策略模式》中的【表单验证】,运用到ajax提交数据验证,效果很棒!

修改上述before方法

var before = function(fn, beforefn) {
  return function() {
    if(beforefn.apply(this, arguments) === false) {
      // beforefn返回false,直接return,不执行后面的原函数
      return;
    }
    return fn.apply(this, arguments);
  };
};
/* 模拟数据验证*/
var validate = function() {
  if(username === "") {
    console.log("验证失败!");
    return false;
  }
  return true;
}
/* 模拟ajax提交*/
var formSubmit = function() {
  console.log("提交!!!");
}
username = 1;
formSubmit = before(formSubmit, validate); // 提交!!!
formSubmit();

username = "";
formSubmit = before(formSubmit, validate); // 验证失败!
formSubmit();

五、装饰者模式和代理模式

相同点:这两种模式都描述了怎么为对象提供一定程度上的间接引用,它们的实现部分都保留了对另外一个对象的引用,并且向那个对象发送请求。
区别:
(1)代理模式:当直接访问本地不方便或者不符合需求时,为这个本体提供一个替代者。本地定义关键功能,而代理提供或拒绝对它的访问,或者在访问本体之前走一些额外的事情。(其做的事情还是跟本体一样)
(2)装饰者模式:为对象动态加入行为。(一开始不能确定对象的全部功能,实实在在的为对象添加新的职责和行为)

希望本文所述对大家学习javascript程序设计有所帮助。

Javascript 相关文章推荐
javascript中利用数组实现的循环队列代码
Jan 24 Javascript
解析John Resig Simple JavaScript Inheritance代码
Dec 03 Javascript
js Array对象的扩展函数代码
Apr 24 Javascript
利用JS进行图片的切换即特效展示图片
Dec 03 Javascript
javascript实现json页面分页实例代码
Feb 20 Javascript
node.js中的buffer.Buffer.byteLength方法使用说明
Dec 10 Javascript
JavaScript对Json的增删改属性详解
Jun 02 Javascript
javascript实现的左右无缝滚动效果
Sep 19 Javascript
JavaScript中如何使用cookie实现记住密码功能及cookie相关函数介绍
Nov 10 Javascript
利用node.js+mongodb如何搭建一个简单登录注册的功能详解
Jul 30 Javascript
详解 vue.js用法和特性
Oct 15 Javascript
vue项目查看vue版本及cli版本的实现方式
Oct 24 Javascript
jQuery事件绑定用法详解(附bind和live的区别)
Jan 19 #Javascript
浏览器环境下JavaScript脚本加载与执行探析之动态脚本与Ajax脚本注入
Jan 19 #Javascript
js实现有过渡渐变效果的图片轮播相册(兼容IE,ff)
Jan 19 #Javascript
jquery 重写 ajax提交并判断权限后 使用load方法报错解决方法
Jan 19 #Javascript
学习JavaScript设计模式之享元模式
Jan 18 #Javascript
纯JavaScript基于notie.js插件实现消息提示特效
Jan 18 #Javascript
学习JavaScript设计模式之责任链模式
Jan 18 #Javascript
You might like
laravel5.0在linux下解决.htaccess无效和去除index.php的问题
2019/10/16 PHP
php框架知识点的整理和补充
2021/03/01 PHP
TNC vs RR BO3 第一场 2.14
2021/03/10 DOTA
在页面上点击任一链接时触发一个事件的代码
2007/04/07 Javascript
js小技巧--自动隐藏红叉叉
2007/08/13 Javascript
js实现的跟随鼠标移动的时钟效果(中英文日期显示)
2011/01/17 Javascript
JavaScript中的连字符详解
2013/11/28 Javascript
jQuery分别获取选中的复选框值的示例
2014/06/17 Javascript
angularJS提交表单(form)
2015/02/09 Javascript
DOM操作一些常用的属性汇总
2015/03/13 Javascript
jquery中$each()方法的使用指南
2015/04/30 Javascript
JS表单验证方法实例小结【电话、身份证号、Email、中文、特殊字符、身份证号等】
2017/02/14 Javascript
纯js实现html转pdf的简单实例(推荐)
2017/02/16 Javascript
微信小程序开发之IOS和Android兼容的问题
2017/09/26 Javascript
JavaScript实现电灯开关小案例
2020/03/30 Javascript
python 时间信息“2018-02-04 18:23:35“ 解析成字典形式的结果代码详解
2018/04/19 Python
啥是佩奇?使用Python自动绘画小猪佩奇的代码实例
2019/02/20 Python
浅谈在JupyterNotebook下导入自己的模块的问题
2020/04/16 Python
Python reduce函数作用及实例解析
2020/05/08 Python
Python-openpyxl表格读取写入的案例详解
2020/11/02 Python
CSS3之多背景background使用示例
2013/10/18 HTML / CSS
纯CSS3制作漂亮带动画效果的主机价格表
2015/04/25 HTML / CSS
使用CSS3制作响应式导航菜单的方法
2015/07/12 HTML / CSS
莫斯科珠宝厂官方网站:Miuz
2020/09/19 全球购物
数学系毕业生的自我评价
2014/01/10 职场文书
工程招投标邀请书
2014/01/26 职场文书
年终总结会议主持词
2014/03/17 职场文书
开学寄语大全
2014/04/08 职场文书
婚内分居协议书范文
2014/11/26 职场文书
建议书范文
2015/02/05 职场文书
行政司机岗位职责
2015/04/10 职场文书
公司财务部岗位职责
2015/04/14 职场文书
2015年大学辅导员工作总结
2015/05/12 职场文书
解决persistence.xml配置文件修改存放路径的问题
2022/02/24 Java/Android
【海涛解说】史上最给力比赛,挑战DOTA极限
2022/04/01 DOTA
向Spring IOC 容器动态注册bean实现方式
2022/07/15 Java/Android