学习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陷阱清单
May 31 Javascript
Jquery动态改变图片IMG的src地址示例
Jun 25 Javascript
javascript中不等于的代码是什么怎么写
Dec 29 Javascript
JQuery实现动态添加删除评论的方法
May 18 Javascript
JavaScript中用sort()方法对数组元素进行排序的操作
Jun 09 Javascript
jQuery插件开发精品教程让你的jQuery提升一个台阶
Jan 27 Javascript
JavaScript编程学习技巧汇总
Feb 21 Javascript
Angular X中使用ngrx的方法详解(附源码)
Jul 10 Javascript
JS实现HTML页面中动态显示当前时间完整示例
Jul 30 Javascript
ES6 迭代器与可迭代对象的实现
Feb 11 Javascript
AutoJs实现刷宝短视频的思路详解
May 22 Javascript
关于对TypeScript泛型参数的默认值理解
Jul 15 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
wamp下修改mysql访问密码的解决方法
2013/05/07 PHP
安装apache2.2.22配置php5.4(具体操作步骤)
2013/06/26 PHP
php+memcache实现的网站在线人数统计代码
2014/07/04 PHP
Ubuntu中启用php的mail()函数并解决发送邮件速度慢问题
2015/03/27 PHP
在线游戏大家来找茬II
2006/09/30 Javascript
Mootools 1.2教程(2) DOM选择器
2009/09/14 Javascript
JavaScript 学习笔记(十三)Dom创建表格
2010/01/21 Javascript
基于jquery的实现简单的表格中增加或删除下一行
2010/08/01 Javascript
按给定几率进行随机抽取的js代码
2010/12/28 Javascript
jQuery 菜单随滚条改为以定位方式(固定要浏览器顶部)
2012/05/24 Javascript
JAVASCRIPT模式窗口中下载文件无法接收iframe的流
2013/10/11 Javascript
js 去除字符串第一位逗号的方法
2014/06/07 Javascript
jquery获取img的src值的简单实例
2016/05/17 Javascript
Actionscript与javascript交互实例程序(修改)
2016/09/22 Javascript
微信小程序 两种滑动方式(横向滑动,竖向滑动)详细及实例代码
2017/01/13 Javascript
关于vue-resource报错450的解决方案
2017/07/24 Javascript
详解微信小程序审核不通过的解决方法
2018/01/17 Javascript
python socket网络编程步骤详解(socket套接字使用)
2013/12/06 Python
Python的类实例属性访问规则探讨
2015/01/30 Python
图文讲解选择排序算法的原理及在Python中的实现
2016/05/04 Python
Python基于Flask框架配置依赖包信息的项目迁移部署
2018/03/02 Python
深入浅析Python获取对象信息的函数type()、isinstance()、dir()
2018/09/17 Python
简单的Python调度器Schedule详解
2019/08/30 Python
Python如何基于Tesseract实现识别文字功能
2020/06/05 Python
树莓派4B安装Tensorflow的方法步骤
2020/07/16 Python
Django实现随机图形验证码的示例
2020/10/15 Python
夏威夷航空官网:Hawaiian Airlines
2016/09/11 全球购物
NULL是什么,它是怎么定义的
2015/05/09 面试题
毕业生找工作自荐书
2014/06/30 职场文书
乡镇平安建设汇报材料
2014/08/25 职场文书
财会专业大学生求职信
2014/09/26 职场文书
工作证明英文模板
2014/10/21 职场文书
说谎欺骗人检讨书300字
2014/11/18 职场文书
2015教师年度思想工作总结
2015/04/30 职场文书
CSS3实现模糊背景的三种效果示例
2021/03/30 HTML / CSS
解决Tkinter中button按钮未按却主动执行command函数的问题
2021/05/23 Python