学习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 10 Javascript
javascript cookies操作集合
Apr 12 Javascript
分享一个自己写的table表格排序js插件(高效简洁)
Oct 29 Javascript
AngularJS中的表单简单入门
Jul 28 Javascript
深入理解JavaScript中Ajax
Aug 02 Javascript
jquery.validate表单验证插件使用方法解析
Nov 07 Javascript
JavaScript中捕获/阻止捕获、冒泡/阻止冒泡方法
Dec 07 Javascript
微信小程序 tabs选项卡效果的实现
Jan 05 Javascript
JS html时钟制作代码分享
Mar 03 Javascript
Angularjs处理页面闪烁的解决方法
Mar 09 Javascript
Javascript 严格模式use strict详解
Sep 16 Javascript
利用js编写网页进度条效果
Oct 08 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的cms
2010/12/19 PHP
php绘制一个扇形的方法
2015/01/24 PHP
Thinkphp3.2.3分页使用实例解析
2016/07/28 PHP
浅谈PHP中如何实现Hook机制
2017/11/14 PHP
PHP按符号截取字符串的指定部分的实现方法
2018/09/10 PHP
JavaScript的parseInt 取整使用
2011/05/09 Javascript
jQuery 关于伪类选择符的使用说明
2013/04/24 Javascript
jquery $.trim()方法使用介绍
2014/05/21 Javascript
Jquery实现仿腾讯微博发表广播
2014/11/17 Javascript
javascript 实现 原路返回
2015/01/21 Javascript
JavaScript编写推箱子游戏
2015/07/07 Javascript
Jqgrid之强大的表格插件应用
2015/12/02 Javascript
js实现人民币大写金额形式转换
2016/04/27 Javascript
node.js实现回调的方法示例
2017/03/01 Javascript
详解node如何让一个端口同时支持https与http
2017/07/04 Javascript
AngularJS实现tab选项卡的方法详解
2017/07/05 Javascript
基于vue v-for 循环复选框-默认勾选第一个的实现方法
2018/03/03 Javascript
微信小程序实现点赞、取消点赞功能
2018/11/02 Javascript
超轻量级的js时间库miment使用解析
2019/08/02 Javascript
vue+animation实现翻页动画
2020/06/29 Javascript
解决vue 使用setTimeout,离开当前路由setTimeout未销毁的问题
2020/07/21 Javascript
swiperjs实现导航与tab页的联动
2020/12/13 Javascript
[01:00:14]DOTA2官方TI8总决赛纪录片 真视界True Sight
2019/01/16 DOTA
利用python获取当前日期前后N天或N月日期的方法示例
2017/07/30 Python
python实现周期方波信号频谱图
2018/07/21 Python
Python3中列表list合并的四种方法
2019/04/19 Python
Python中的字符串切片(截取字符串)的详解
2019/05/15 Python
基于python获取本地时间并转换时间戳和日期格式
2020/10/27 Python
纯CSS3实现带动画效果导航菜单无需js
2013/09/27 HTML / CSS
钉钉企业内部H5微应用开发详解
2020/05/12 HTML / CSS
亚洲航空公司官方网站:AirAsia
2019/11/25 全球购物
学前教育毕业生自荐信
2013/10/29 职场文书
工程管理专业个人求职信范文
2013/12/07 职场文书
重阳节慰问信
2015/02/15 职场文书
2015年测量员工作总结
2015/05/23 职场文书
2015年教师国培感言
2015/08/01 职场文书