装饰者模式

最近在读《javascript设计模式和开发实践》,记录一下自己的理解。

定义

装者,藏也,饰者,物既成加以文采也。装饰者模式就是给对象添加更多的功能而不改变对象本身。

装饰函数

函数是javascript中的一等对象,我们给对象添加额外的方法很简单,给函数添加额外的方法,确很难。
我们通过保留原引用的方式可以还写某个函数。

    var animal = function () {
        console.log('动物')
    }

    var _animal = animal; // 保存原有的引用方式

    animal = function () {
        _animal();
        console.log('植物')
    }

    animal()

这是最常见的一种做法,但是会有两个缺点。

  • 需要装饰的函数变多时,会有大量的中间变量需要维护。
  • this劫持
  var obj = {
        say: function () {
            this.content()
        },
        content: function () {
            console.log('我正在说' + this)
        }
    }

    obj.say()
    // 我正在说[object Object]

    var _say = obj.say;  

    obj.say = function () {
        console.log(this)
        _say()
    }
    obj.say()

    // Uncaught TypeError: this.content is not a function
    // 根本原因是_say变量只是单纯的引用函数,不会附带函数的上下文

我们可以在函数执行的时候绑定手动绑定this。

    obj.say = function () {
        // _say.call(obj, arguments)

        _say.bind(this)() // 这里的this指的是obj所以可以正常执行
    }
    obj.say()

    // 我正在说[object Object]

用AOP装饰函数

AOP是面向切面编程,可以将业务逻辑各个部分隔离,降低耦合程度,提高可重用性。

    Function.prototype.before = function (beforeFn) {
        var _self = this; // 保存对原函数的引用  这个this指的是调用before函数的对象
        return function () {
            beforeFn.apply(this, arguments); // 这个this指的是当前function 所处的上下文不做特殊处理指的是window arguments也指的是当前这个匿名函数的参数
            return _self.apply(this, arguments)
        }
    }

    function personal(str) {
        console.log(`我是${str}`)
    }

    personal('man')
    personal.before(function () {
        console.log(arguments)
        console.log('我是人')
    })('woman')  // 上文中的arguments指的是现在的woman

上面的代码我们给Function的原型上添加了before方法,personal可以调用,我们在没有改变personal的前提下,personal函数有了额外的功能。

实例

  • 数据上报

    
    var log = function () {
        alert('开始上报数据')
    }
    
    var login = function () {
        alert('打开登录弹窗');
        log()
    }
    
    login()
    
    // 这样写虽然可以实现但业务代码和数据统计的代码耦合在一起
    
    var after = function (fn, afterFn) {
        return function () {
            let ret = fn.apply(this, arguments)
            afterFn.apply(this, arguments);
            return ret;
        }
    }
    
    login = function () {
        alert('登录')
    }
    log = function () {
        alert('上报')
    }
    
    login = after(login, log)
    login()
  • 动态改变参数(因为before函数和_self调用的arguments是同一个,我们若将arguments指向一个对象即可改变参数)

    var param = {
        name: '小明'
    }
    
    var changeName = function (param) {
        console.log(param.name)
    }
    
    changeName.before(function () {
        arguments[0].name='小芳';  // arguments[0]只是对param的引用两个变量指向同一个对象,故会改变name的值
    })(param)
    // 小芳

    装饰者适用场景

  • 接手新项目不熟悉业务具体代码,要添加额外的功能。
  • 不同业务逻辑相互交叉。
  • 没事重构代码————装逼。

版权声明:本文为mzqn原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/mzqn/p/9077335.html