• linkedu视频
  • 平面设计
  • 电脑入门
  • 操作系统
  • 办公应用
  • 电脑硬件
  • 动画设计
  • 3D设计
  • 网页设计
  • CAD设计
  • 影音处理
  • 数据库
  • 程序设计
  • 认证考试
  • 信息管理
  • 信息安全
菜单
linkedu.com
  • 网页制作
  • 数据库
  • 程序设计
  • 操作系统
  • CMS教程
  • 游戏攻略
  • 脚本语言
  • 平面设计
  • 软件教程
  • 网络安全
  • 电脑知识
  • 服务器
  • 视频教程
  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号
您的位置:首页 > 程序设计 >JavaScript > js设计模式-- 装饰着模式

js设计模式-- 装饰着模式

作者:青春的小白 字体:[增加 减小] 来源:互联网 时间:2017-09-05

青春的小白通过本文主要向大家介绍了设计模式等相关知识,希望对您有所帮助,也希望大家支持linkedu.com www.linkedu.com

在不改变对象自身的基础上,在程序运行期间给对象动态地添加一些额外职责
在传统面向对象语言中,为对象添加功能常使用继承
但是继承有很多缺点:

 

超类子类强耦合,超类改变导致子类改变
超类内部细节对子类可见,破坏了封装性
完成功能复用同时,可能会创造大量子类
前两点都很好理解
关于最后一点
比如说我们为五种不同类型的房子定义了五个类
但是我们还需要为每个房子添加门、窗户、喷漆
如果使用继承,那我们需要添加的子类个数就是:5×3 =15
这就非常的麻烦
但是如果我们可以动态的将门、窗户、喷漆添加到各房子对象
那么我们只需要这三个类

这样动态向对象添加职责的方式就是装饰者模式
就好比天冷了,我们穿上一层层衣服

简单实现

对于我们动态的解释语言JavaScript,要实现起来很容易
我们一步一步来实现

首先装饰者模式不是动态添加职责么

var person = {
    name: 'payen',
    sex: 'male'
}
person.age = '20';

当然没有这么简单
最上面我就说了
装饰者模式是在不改变对象自身的基础上
而我们改变了原对象
那我们再做进一步改变

现在我们假设正在开发一个小游戏:雷霆战机
最开始我们使用最渣的飞机,随着击落飞机吃道具
火力越来越NB
吃一颗星,不仅可以发普通子弹,还会发射散弹
再吃一颗,不仅可以发射普通子弹和散弹,还有跟踪导弹

var plane = {
    fire: function(){
        console.log('发射子弹');
    }
}
plane.fire();
//发射子弹
var fire1 = plane.fire;
var shot = function(){
    console.log('发射散弹');
}
plane.fire = function(){
    fire1();
    shot();
}
plane.fire();
//发射子弹 发射散弹
var fire2 = plane.fire;
var track = function(){
    console.log('发射跟踪导弹');
}
plane.fire = function(){
    fire2();
    track();
}
plane.fire();

//发射子弹 发射散弹 发射跟踪导弹
这样给对象动态的增加职责的方式就没有改变对象自身
一个对象放入另一个对象
形成了一条装饰链(一个聚合对象)
而上面的shot和track也就是是装饰者、装饰函数
当函数执行时,会把请求转给链中的下一个对象

函数功能扩展

在javascript中,很容易给对象扩展属性与方法
但是却不容易给函数扩展额外功能,除非改函数源码
但是改写函数违反了开放-封闭原则

var foo = function(){
    console.log(1);
}
//改为
var foo = function(){
    console.log(1);
    console.log(2);//增
}

一个常用的方法就是缓存函数引用,改写函数

var foo = function(){
    console.log(1);
}

//改为
var foo = function(){
    console.log(1);
}
var _foo = foo;
foo = function(){
    _foo();
    console.log(2);
}

但是这样写还是存在问题

要维护额外的中间变量(_foo),如果装饰链过长,中间变量就会越来越多
可能会存在this被劫持问题
关于this劫持问题,看下面的例子

var getId = document.getElementById;
document.getElementById = function(ID){
    console.log(1);
    return getId(ID);
}
document.getElementById('demo');

这样浏览器会报错

为什么呢?
因为使用 document.getElementById 的时候
内部有this引用,而这个this期望指向的是document
但是 getId 在获取了 document.getElementById 引用后
this就指向了window,导致抛出错误

为了让this正确指向document
我们可以做出修改

var getId = document.getElementById;
document.getElementById = function(ID){
    console.log(1);
    return getId.call(document, ID);
}
document.getElementById('demo');

但是这样还是很麻烦
下面我们通过AOP来为装饰者模式实现一个完美的解决方案

AOP装饰函数

先来给大家科普一下什么是AOP

AOP(Aspect Oriented Programming)面向切面编程
把一些与核心业务逻辑无关的功能抽离出来
再通过“动态织入”方式掺入业务逻辑模块
与业务逻辑无关的功能通常包括日志统计、安全控制、异常处理等等
好处也很明显,保证了核心业务模块的纯净与高内聚性
而且其他的功能模块也可以很好的复用

首先我们要实现两个函数
一个用来前置装饰,一个用来后置装饰

Function.prototype.before = function(beforeFunc){
    var that = this;
    return function(){
        beforeFunc.apply(this, arguments);
        return that.apply(this, arguments);
    }
}
Function.prototype.after = function(afterFunc){
    var that = this;
    return function(){
        var ret = that.apply(this, arguments);
        afterFunc.apply(this, arguments);
        return ret;
    }
}

以前置装饰为例
调用before时,先把原函数的引用保存下来
然后返回一个“代理”函数
这样在原函数调用前,先执行扩展功能的函数
而且他们共用同一个参数列表
后置装饰与前置装饰基本类似,只是执行顺序不同

如果不喜欢这种污染原型的方式,也可以这么写

var before = function(originFunc, beforeFunc){
    return function(){
        beforeFunc.apply(this, arguments);
        return originFunc.apply(this, arguments);
    }
}
var after = function(originFunc, afterFunc){
    return function(){
        var ret = originFunc.apply(this, arguments);
        afterFunc.apply(this, arguments);
        return ret;
    }
}

使用这种AOP的方式可以完美的对函数进行功能扩展

var foobar = function(x, y, z){
    console.log(x, y, z);
}
var foo = function(x, y, z){
    console.log(x/10, y/10, z/10);
}
var bar = function(x, y, z){
    console.log(x*10, y*10, z*10);
}
foobar = foobar.before(foo).after(bar);
foobar(1, 2, 3);
//0.1 0.2 0.3
//1 2 3
//10 20 30

由于原函数和装饰函数共用一个参数列表
所以我们可以用AOP改变函数参数

var data = {
    width: '100px',
    height: '100px'
}
var demo = function(data){
    console.log(JSON.stringify(data));
}
demo = demo.before(function(data){
    data.color = 'red';
});
demo(data);
//{"width":"100px","height":"100px","color":"red"}

最后

最后谈一谈装饰者模式的缺点
它也不是十全十美的

装饰链叠加了函数作用域,如果过长也会产生性能问题
如果原函数上保存了属性,返回新函数后属性会丢失

var demo = function(){
    console.log(1);
}
demo.a = 123;
demo = demo.after(function(){
    console.log(2);
});
demo();
console.log(demo.a);
//undefined

装饰者模式在开发中非常有用,在框架开发中也十分有用

分享到:QQ空间新浪微博腾讯微博微信百度贴吧QQ好友复制网址打印

您可能想查找下面的文章:

相关文章

  • 2017-05-11基于JS实现bookstore静态页面的实例代码
  • 2017-05-11D3.js中强制异步文件读取同步的几种方法
  • 2017-05-11微信小程序 参数传递实例代码
  • 2017-05-11js实现一键复制功能
  • 2017-05-11AngularJS $http模块POST请求实现
  • 2017-05-11nodejs 终端打印进度条实例代码
  • 2017-05-11js时间戳格式化成日期格式的多种方法介绍
  • 2017-05-11详解支持Angular 2的表格控件
  • 2017-05-11详解vue2路由vue-router配置(懒加载)
  • 2017-05-11js实现数组去重方法及效率對比

文章分类

  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号

最近更新的内容

    • jquery实现图片上传前本地预览
    • 纯JS实现弹性导航条效果
    • MUI 上拉刷新/下拉加载功能实例代码
    • 原生js实现倒计时--2018
    • 教你用十行node.js代码读取docx的文本
    • js实现带简单弹性运动的导航条
    • angularjs中回车键触发某一事件的方法
    • 详解node.js平台下Express的session与cookie模块包的配置
    • Bootstarp 基础教程之表单部分实例代码
    • jQuery插件HighCharts绘制2D带Label的折线图效果示例【附demo源码下载】

关于我们 - 联系我们 - 免责声明 - 网站地图

©2020-2025 All Rights Reserved. linkedu.com 版权所有