学习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 相关文章推荐
jquery表单验证使用插件formValidator
Nov 10 Javascript
在表单提交前进行验证的几种方式整理
Jul 31 Javascript
jQuery is()函数用法3例
May 06 Javascript
javascript 实现map集合
Apr 03 Javascript
DOM 高级编程
May 06 Javascript
简单谈谈javascript中this的隐式绑定
Feb 22 Javascript
vue获取DOM元素并设置属性的两种实现方法
Sep 30 Javascript
Vue前端开发规范整理(推荐)
Apr 23 Javascript
JavaScript引用类型RegExp基本用法详解
Aug 09 Javascript
云服务器部署Node.js项目的方法步骤(小白系列)
Mar 23 Javascript
基于element-ui对话框el-dialog初始化的校验问题解决
Sep 11 Javascript
vue实现简单计算商品价格
Sep 14 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
UTF8编码内的繁简转换的PHP类
2009/07/09 PHP
PHP新手用的Insert和Update语句构造类
2012/03/31 PHP
php获取文件类型和文件信息的方法
2015/07/10 PHP
PHP二维数组去重算法
2016/12/17 PHP
javascript document.images实例
2008/05/27 Javascript
LazyLoad 延迟加载(按需加载)
2010/05/31 Javascript
基于jQuery实现多标签页切换的效果(web前端开发)
2016/07/24 Javascript
node.js 抓取代理ip实例代码
2017/04/30 Javascript
node.JS md5加密中文与php结果不一致的解决方法
2017/05/05 Javascript
兼容浏览器的js事件绑定函数(详解)
2017/05/09 Javascript
使用classList来实现两个按钮样式的切换方法
2018/01/24 Javascript
微信小程序之圆形进度条实现思路
2018/02/22 Javascript
详解JSON Web Token 入门教程
2018/07/30 Javascript
[06:21]完美世界亚洲区首席发行官竺琦TI3采访
2013/08/26 DOTA
Python SQLite3数据库操作类分享
2014/06/10 Python
python读取json文件并将数据插入到mongodb的方法
2015/03/23 Python
Python实现将xml导入至excel
2015/11/20 Python
MySQL中表的复制以及大型数据表的备份教程
2015/11/25 Python
使用TensorFlow-Slim进行图像分类的实现
2019/12/31 Python
python+selenium+chrome批量文件下载并自动创建文件夹实例
2020/04/27 Python
如何完美的建立一个python项目
2020/10/09 Python
Python 实现PS滤镜中的径向模糊特效
2020/12/03 Python
小程序瀑布流解决左右两边高度差距过大的问题
2019/02/20 HTML / CSS
俄罗斯名牌服装网上商店:UNIQUE FABRIC
2019/07/25 全球购物
西班牙品牌鞋子、服装和配饰在线商店:Esdemarca
2021/02/17 全球购物
女大学生毕业找工作的自我评价
2013/10/03 职场文书
日本语毕业生自荐信
2014/02/01 职场文书
财务人员的自我评价范文
2014/03/03 职场文书
婚庆公司计划书
2014/09/15 职场文书
工艺技术员岗位职责
2015/02/04 职场文书
2015年入党决心书
2015/02/05 职场文书
无故旷工检讨书
2015/08/15 职场文书
Vue中插槽slot的使用方法与应用场景详析
2021/06/08 Vue.js
php去除deprecated的实例方法
2021/11/17 PHP
javascript之Object.assign()的痛点分析
2022/03/03 Javascript
Python&Matlab实现灰狼优化算法的示例代码
2022/03/21 Python