学习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消除window.close()的提示窗口
May 20 Javascript
javascript实现自动输出文本(打字特效)
Aug 27 Javascript
jQuery中的siblings用法实例分析
Dec 24 Javascript
JS实现简单的右下角弹出提示窗口完整实例
Jun 21 Javascript
Bootstrap轮播插件简单使用方法介绍
Jun 21 Javascript
用jquery快速解决IE输入框不能输入的问题
Oct 04 Javascript
简单实现JavaScript图片切换效果
Nov 28 Javascript
利用Angular.js编写公共提示模块的方法教程
May 28 Javascript
webpack4+express+mongodb+vue实现增删改查的示例
Nov 08 Javascript
Vue 中 template 有且只能一个 root的原因解析(源码分析)
Apr 11 Javascript
JavaScript异步操作的几种常见处理方法实例总结
May 11 Javascript
vue如何在用户要关闭当前网页时弹出提示的实现
May 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使用sql server验证连接数据库的方法
2014/12/25 PHP
PHP二维数组去重算法
2016/12/17 PHP
PHP实现函数内修改外部变量值的方法示例
2018/12/28 PHP
php中加密解密DES类的简单使用方法示例
2020/03/26 PHP
jQuery 使用手册(七)
2009/09/23 Javascript
基于JQuery的浮动DIV显示提示信息并自动隐藏
2011/02/11 Javascript
js中字符替换函数String.replace()使用技巧
2011/08/14 Javascript
详解JavaScript对Date对象的操作问题(生成一个倒数7天的数组)
2015/10/01 Javascript
JavaScript设置、获取、清除单值和多值cookie的方法
2015/11/17 Javascript
Bootstrap carousel轮转图的使用实例详解
2016/05/17 Javascript
EasyUI中的dataGrid的行内编辑
2017/06/22 Javascript
JavaScript 数组的进化与性能分析
2017/09/18 Javascript
mui框架 页面无法滚动的解决方法(推荐)
2018/01/25 Javascript
浅谈vue中.vue文件解析流程
2018/04/24 Javascript
Bootstrap-table自定义可编辑每页显示记录数
2018/09/07 Javascript
ES2020 新特性(种草)
2020/01/12 Javascript
JS校验与最终登陆界面功能完整示例
2020/01/13 Javascript
[00:33]DOTA2上海特级锦标赛 CDEC战队宣传片
2016/03/04 DOTA
Python类的多重继承问题深入分析
2014/11/09 Python
python+pyqt实现12306图片验证效果
2017/10/25 Python
python扫描线填充算法详解
2020/02/19 Python
基于python实现监听Rabbitmq系统日志代码示例
2020/11/28 Python
css3实现的下拉菜单效果示例
2014/01/22 HTML / CSS
css3中transition属性详解
2014/09/02 HTML / CSS
css3一个简易的 LED 数字时钟实现方法
2020/01/15 HTML / CSS
英国最大的纸工艺品商店:CraftStash
2018/12/01 全球购物
美国豪华的多品牌精品店:The Webster
2019/07/31 全球购物
诚信承诺书模板
2014/05/26 职场文书
普通党员个人整改措施
2014/10/27 职场文书
幼儿园班级工作总结2015
2015/05/25 职场文书
2016入党心得体会范文
2016/01/06 职场文书
导游词之镇江焦山
2019/11/21 职场文书
python实现的web监控系统
2021/04/27 Python
《辉夜大小姐想让我告白》第三季正式预告
2022/03/20 日漫
Python实现提取PDF简历信息并存入Excel
2022/04/02 Python
微软团队与 NASA 科学家和惠普企业(HPE)的工程师合作
2022/04/21 数码科技