学习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中获得$.ajax()事件返回的值并添加事件的方法
Apr 15 Javascript
javascript中创建对象的三种常用方法
Dec 30 Javascript
Jquery刷新页面背景图片随机变换的实现方法
Mar 15 Javascript
javascript中兼容主流浏览器的动态生成iframe方法
May 05 Javascript
QQ空间顶部折页撕开效果示例代码
Jun 15 Javascript
Easyui 之 Treegrid 笔记
Apr 29 Javascript
微信小程序 label 组件详解及简单实例
Jan 10 Javascript
JS判断指定dom元素是否在屏幕内的方法实例
Jan 23 Javascript
浅谈Vue Element中Select下拉框选取值的问题
Mar 01 Javascript
javascript实现计算指定范围内的质数示例
Dec 29 Javascript
详解使用JWT实现单点登录(完全跨域方案)
Aug 02 Javascript
jQuery实现html可联动的百分比进度条
Mar 26 jQuery
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的网址
2006/11/25 PHP
深入浅析php中sprintf与printf函数的用法及区别
2016/01/08 PHP
php单例模式的简单实现方法
2016/06/10 PHP
php删除数组指定元素实现代码
2017/05/03 PHP
Jquery 表格合并的问题分享
2011/09/17 Javascript
jQuery源码分析-03构造jQuery对象-源码结构和核心函数
2011/11/14 Javascript
Js数组的操作push,pop,shift,unshift等方法详细介绍
2012/12/28 Javascript
浅谈javascript中关于日期和时间的基础知识
2016/07/13 Javascript
Ajax和Comet技术总结
2017/02/19 Javascript
不使用JavaScript实现菜单的打开和关闭效果demo
2018/05/01 Javascript
Nodejs使用Mongodb存储与提供后端CRD服务详解
2018/09/04 NodeJs
Node如何后台数据库使用增删改查功能
2019/11/21 Javascript
vue路由权限校验功能的实现代码
2020/06/07 Javascript
基于jquery实现彩色投票进度条代码解析
2020/08/26 jQuery
Vue中用JSON实现刷新界面不影响倒计时
2020/10/26 Javascript
python原始套接字编程示例分享
2014/02/21 Python
python使用post提交数据到远程url的方法
2015/04/29 Python
python正则表达式之作业计算器
2016/03/18 Python
Python检测网站链接是否已存在
2016/04/07 Python
Python简单获取自身外网IP的方法
2016/09/18 Python
python爬虫获取淘宝天猫商品详细参数
2020/06/23 Python
python制作mysql数据迁移脚本
2019/01/01 Python
Python Django切换MySQL数据库实例详解
2019/07/16 Python
Pytorch mask_select 函数的用法详解
2020/02/18 Python
Python Django中的STATIC_URL 设置和使用方式
2020/03/27 Python
Django使用list对单个或者多个字段求values值实例
2020/03/31 Python
python 中的命名空间,你真的了解吗?
2020/08/19 Python
咖啡为什么会有酸味?你喝到的咖啡為什麼是酸的?
2021/03/17 冲泡冲煮
瑞典在互联网上最大的宠物商店:Animail
2020/10/31 全球购物
法学专业应届生求职信
2013/10/16 职场文书
党员岗位承诺口号大全
2014/03/28 职场文书
书香校园建设方案
2014/05/02 职场文书
安全责任书怎么写
2014/07/28 职场文书
八年级英语教学计划
2015/01/23 职场文书
退伍军人感言
2015/08/01 职场文书
详解gantt甘特图可拖拽、编辑(vue、react都可用 highcharts)
2021/11/27 Vue.js