学习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 检测、添加、移除样式(className)函数代码
Sep 08 Javascript
javascript中数组(Array)对象和字符串(String)对象的常用方法总结
Dec 15 Javascript
深入理解Javascript箭头函数中的this
Feb 13 Javascript
利用JavaScript如何查询某个值是否数组内
Jul 30 Javascript
vue多种弹框的弹出形式的示例代码
Sep 18 Javascript
vue.js或js实现中文A-Z排序的方法
Mar 08 Javascript
vue使用监听实现全选反选功能
Jul 06 Javascript
vue 根据数组中某一项的值进行排序的方法
Aug 30 Javascript
vue弹窗组件的实现示例代码
Sep 10 Javascript
js利用递归与promise 按顺序请求数据的方法
Aug 30 Javascript
vue下axios拦截器token刷新机制的实例代码
Jan 17 Javascript
vue自定义指令限制输入框输入值的步骤与完整代码
Aug 30 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
PHP小教程之实现双向链表
2014/06/12 PHP
php上传文件并显示上传进度的方法
2015/03/24 PHP
学习thinkphp5.0验证类使用方法
2017/11/16 PHP
JS 巧妙获取剪贴板数据 Excel数据的粘贴
2009/07/09 Javascript
html+css+js实现xp window界面及有关功能
2013/03/26 Javascript
jQuery实现列表自动循环滚动鼠标悬停时停止滚动
2013/09/06 Javascript
jquery实现倒计时代码分享
2014/06/13 Javascript
JQuery判断radio是否选中并获取选中值的示例代码
2014/10/17 Javascript
JQuery设置时间段下拉选择实例
2014/12/30 Javascript
详解Bootstrap四种图片样式
2016/01/04 Javascript
使用BootStrap建立响应式网页——通栏轮播图(carousel)
2016/12/21 Javascript
loading动画特效小结
2017/01/22 Javascript
用js实现before和after伪类的样式修改的示例代码
2017/09/07 Javascript
基于JavaScript实现简单的音频播放功能
2018/01/07 Javascript
解决vue 中 echart 在子组件中只显示一次的问题
2018/08/07 Javascript
JavaScript canvas实现雨滴特效
2021/01/10 Javascript
[02:08:58]2014 DOTA2国际邀请赛中国区预选赛 Ne VS CIS
2014/05/22 DOTA
Python素数检测的方法
2015/05/11 Python
浅谈Python 字符串格式化输出(format/printf)
2016/07/21 Python
python使用 HTMLTestRunner.py生成测试报告
2017/10/20 Python
Python字符串、整数、和浮点型数相互转换实例
2018/08/04 Python
详解Django中类视图使用装饰器的方式
2018/08/12 Python
python实现彩色图转换成灰度图
2019/01/15 Python
python实现多层感知器
2019/01/18 Python
python模块之subprocess模块级方法的使用
2019/03/26 Python
Python获取、格式化当前时间日期的方法
2020/02/10 Python
Python实现链表反转的方法分析【迭代法与递归法】
2020/02/22 Python
如何提高python 中for循环的效率
2020/04/15 Python
Django使用Profile扩展User模块方式
2020/05/14 Python
使用Python快速打开一个百万行级别的超大Excel文件的方法
2021/03/02 Python
英国最大的电脑零售连锁店集团:PC World
2016/10/10 全球购物
机关办公室岗位职责
2014/04/16 职场文书
3的组成教学反思
2014/04/30 职场文书
幼儿园家长安全责任书
2014/07/22 职场文书
工作汇报材料难写?方法都在这里了!
2019/07/01 职场文书
再读《皇帝的新衣》的读后感悟!
2019/08/07 职场文书