学习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 相关文章推荐
FormValidate 表单验证功能代码更新并提供下载
Aug 23 Javascript
33种Javascript 表格排序控件收集
Dec 03 Javascript
关于JavaScript作用域你想知道的一切
Feb 04 Javascript
第三章之Bootstrap 表格与按钮功能
Apr 25 Javascript
利用VUE框架,实现列表分页功能示例代码
Jan 12 Javascript
jQuery序列化后的表单值转换成Json
Jun 16 jQuery
前端必备插件之纯原生JS的瀑布流插件Macy.js
Nov 22 Javascript
react-router v4如何使用history控制路由跳转详解
Jan 09 Javascript
[原创]jQuery实现合并/追加数组并去除重复项的方法
Apr 11 jQuery
基于JavaScript实现简单抽奖功能代码实例
Oct 20 Javascript
微信小程序自定义底部弹出框功能
Nov 18 Javascript
Javascript生成器(Generator)的介绍与使用
Jan 31 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学习笔记 PHP面向对象的程序设计
2011/06/13 PHP
php实现转换html格式为文本格式的方法
2016/05/16 PHP
10款新鲜出炉的 jQuery 插件(Ajax 插件,有幻灯片、图片画廊、菜单等)
2011/06/08 Javascript
js定时器的使用(实例讲解)
2014/01/06 Javascript
Egret引擎开发指南之创建项目
2014/09/03 Javascript
JavaScript中return用法示例
2016/11/29 Javascript
form表单序列化详解(推荐)
2017/08/15 Javascript
10个在JavaScript开发中常遇到的BUG
2017/12/18 Javascript
webpack 开发和生产并行设置的方法
2018/11/08 Javascript
javascript防抖函数debounce详解
2019/06/11 Javascript
vue的路由映射问题及解决方案
2019/10/14 Javascript
jQuery实现颜色打字机的完整代码
2020/03/19 jQuery
vue动态设置页面title的方法实例
2020/08/23 Javascript
如何使用原生Js实现随机点名详解
2021/01/06 Javascript
[39:32]2014 DOTA2国际邀请赛中国区预选赛 TongFu VS DT 第二场
2014/05/23 DOTA
[02:26]2016国际邀请赛8月3日开战 中国军团出征西雅图
2016/08/02 DOTA
videocapture库制作python视频高速传输程序
2013/12/23 Python
python3批量删除豆瓣分组下的好友的实现代码
2016/06/07 Python
python脚本爬取字体文件的实现方法
2017/04/29 Python
Tornado 多进程实现分析详解
2018/01/12 Python
Python退火算法在高次方程的应用
2018/07/26 Python
python2 与 pyhton3的输入语句写法小结
2018/09/10 Python
python中嵌套函数的实操步骤
2019/02/27 Python
不归路系列:Python入门之旅-一定要注意缩进!!!(推荐)
2019/04/16 Python
python3下载抖音视频的完整代码
2019/06/05 Python
如何使用python实现模拟鼠标点击
2020/01/06 Python
PyQt5 closeEvent关闭事件退出提示框原理解析
2020/01/08 Python
python 工具 字符串转numpy浮点数组的实现
2020/03/14 Python
Python新手学习装饰器
2020/06/04 Python
自考毕业生自我鉴定
2013/11/04 职场文书
2014年消防工作实施方案
2014/02/20 职场文书
励志广播稿300字(5篇)
2014/09/15 职场文书
2014年医院科室工作总结
2014/12/20 职场文书
SQL Server实现分页方法介绍
2022/03/16 SQL Server
动画《平凡职业成就世界最强》宣布制作OVA
2022/04/01 日漫
Python批量解压&压缩文件夹的示例代码
2022/04/04 Python