yizihan / blog

Record
1 stars 0 forks source link

AOP - 面向切面编程 #11

Open yizihan opened 6 years ago

yizihan commented 6 years ago

AOP

AOP(Aspect Oriented Program):在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想。

创建before和after两个方法,分别可以在函数的前后无侵入的执行相关逻辑

// 功能函数
function test() {
    console.log(2);
}
function beforeFunc() {
    console.log(1)
}
function afterFunc() {
    console.log(3)
}

// 给函数原型添加before方法 (函数调用函数)
// before => 先执行回调,再执行功能函数
Function.prototype.before = function (fn) {
    var _self = this;            // this指向调用当前方法的函数 即test()
    fn();                    // 首先,执行before方法传入的函数
    _self.apply(this, arguments);// 然后,继续执行功能函数
    // ==> test.apply(this, arguments);
}

// after => 先执行功能函数,再执行回调
Function.prototype.after = function (fn) {
    var _self = this;
    _self.apply(this, arguments);   // 先执行调用当前方法的函数
    fn();                           // 再执行当前方法指定的函数
};

// test()被执行了两次
test.before(beforeFunc)     // 1 2
test.after(afterFunc)       // 2 3

存在的问题: 上述逻辑在同时调用before和after时,功能函数会发生重复调用。

解决方案 - 链式调用

before 回调和功能函数一起送到after after 功能函数和回调一起送到before

Function.prototype.before = function(fn) {
    console.log('aaa')
    var _self = this;
    // 因为before和after被添加到了原型链上
    // 此时的匿名函数也有before和after方法
    return function() {     // 链式调用 将内容作为返回值推给后面的函数
        console.log('ddd')
        // 执行beforeFunc函数 console.log(1)
        if (fn.apply(_self, arguments) === false) {
            return false;
        }
        console.log('eee')
        return _self.apply(_self, arguments)    // 执行test函数 console.log(2)
        // 如果test函数有返回值,则将返回值return
    }
};

Function.prototype.after = function(fn) {
    console.log('bbb')
    var _self = this;
    return function() {     // 链式调用 将内容作为返回值推给后面的函数
        console.log('ccc')
        // 将afterFunc函数缓存起来
        // _self.apply(_self, arguments) 执行before中return的内容
        var ret = _self.apply(_self, arguments);    // 得到before函数return内容的return内容 即test函数的返回值 如果返回值为false,则停止运行当前函数
        console.log('fff')
        if (ret === false) {
            return false;
        }
        console.log('ggg')
        fn.apply(_self, arguments); // 执行afterFunc函数 console.log(3)
        console.log('hhh')
        return ret;
    }
};
test.before(beforeFunc).after(afterFunc)(); 
// aaa bbb ccc ddd 1 eee 2 fff ggg 3 hhh

test.after(beforeFunc).before(afterFunc)(); 
// bbb aaa ddd 3 eee ccc 2 fff ggg 1 hhh

利用链式调用,将前面的 函数内容 作为后面函数体中的_self.apply(_self, arguments)调用,解决重复调用问题

注意闭包的this指向、执行作用域和执行栈