JavaScript设计模式 装饰者模式

一.生活场景

现在有 4 种型号的自行车,我们为每种自行车都定义了一个单独的类。现在要给每种自行都装上前灯、尾灯和铃铛这 3 种配件。如果使用继承的方式来给每种自行车创建子类,则需要 4×3 = 12 个子类。但是如果把前灯、尾灯、铃铛这些对象动态组合到自行车上面,则只需要额外增加3 个类。这种给对象动态地增加职责的方式称为装饰者(decorator)模式。

二.定义

装饰者(decorator)模式:动态地给某个对象添加一些额外的职责,而不会影响从这个类中派生的其他对象。

  • 在不改变原有对象的基础之上,将功能附加到对象上。
  • 提供比继承更具有弹性的替代方法。

三.开发实践

1.装饰函数
很多时候,由于种种原因,我们不想去碰原函数,可以通过保存原引用的方式改写原函数。

window.onload = function () {
     
    alert(1);
}
var _onload = window.onload || function () {
      };
window.onload = function () {
     
	alert(2);
    _onload();
} 

我们给onload函数前添加了输出2的功能,虽然我们在增加新功能的时候,没有修改原来的window.onload 代码,但是存在一些弊端:
1)必须维护_onload 这个中间变量。
2)可能会遇到 this 被劫持的问题。
this劫持问题:

var _getElementById = document.getElementById;
document.getElementById = function( id ){
     
	alert (1);
	return _getElementById( id ); // (1)
}
var button = document.getElementById( 'button' ); // 输出: Uncaught TypeError: Illegal invocation

原因:document.getElementById 方法的内部实现需要使用 this 引用(需指向document对象),而此时_getElementById的this指向window对象。
2.AOP 装饰函数
使用AOP装饰函数,提供一种完美的方法给函数动态增加功能,解决上述的困扰。
AOP(面向切面编程)的主要作用是把一些跟核心业务逻辑模块无关的功能抽离出来,再通过“动态织入”的方式掺入业务逻辑模块中。

/********** ***********    AOP实现     ************************/
Function.prototype.before = function (beforefn) {
     
    var __self = this; // 保存原函数的引用
    return function () {
      // 返回包含了原函数和新函数的"代理"函数
        beforefn.apply(this, arguments); // 执行新函数,且保证 this 不被劫持,新函数接受的参数
        // 也会被原封不动地传入原函数,新函数在原函数之前执行
        return __self.apply(this, arguments); // 执行原函数并返回原函数的执行结果,
        // 并且保证 this 不被劫持
    }
}
Function.prototype.after = function (afterfn) {
     
    var __self = this;
    return function () {
     
        var ret = __self.apply(this, arguments);
        afterfn.apply(this, arguments);
        return ret;
    }
}; 

下面我们利用AOP实现一个应用实例。
3.数据统计上报

<html>
<button tag="login" id="button">点击打开登录浮层button>
<script>
    Function.prototype.after = function (afterfn) {
      
        var __self = this;
        return function () {
      
            var ret = __self.apply(this, arguments);
            afterfn.apply(this, arguments);
            return ret;
        }
    };
    var showLogin = function () {
      
        console.log('打开登录浮层');
    }
    var log = function () {
      
        console.log('上报标签为: ' + this.getAttribute('tag'));
    }
    showLogin = showLogin.after(log); // 打开登录浮层之后上报数据
    document.getElementById('button').onclick = showLogin;
script>

html>

通常情况下 showLogin 函数里,既要负责打开登录浮层,又要负责数据上报,这是两个层面的功能。使用 AOP 分离之后,解耦了两个功能。

四.总结

使用装饰函数就是JavaScript 中的装饰者模式。我们可以写多个装饰函数,将它们加在原函数的执行前后,自由组合。它在框架开发中也十分有用,我们希望框架里的函数提供的是一些稳定而方便移植的功能,那些个性化的功能可以在框架之外动态装饰上去。

你可能感兴趣的:(JavaScript设计模式,设计模式,js)