# AOP切面编程

涉及知识点:

  • 闭包(词法作用域产生是根据函数定义的位置,执行上下文是执行的时候观察的)
  • 词法作用域和执行上下文的概念。
  • this指向,箭头函数没有this,arguments和原型
  • 扩展运算符和用apply(this,arguments)来实现
  • call的用法,改变this指向,让函数执行

AOP切面编程的思想在前端很多地方使用频繁,需要对基础功非常扎实。

AOP(面向切面编程)的主要作用是把一些跟核心业务逻辑模块无关的功能抽离出来,其实就是给原函数增加一层,不用管原函数内部实现

常见:1.vue的对数组实现响应式,用此法去完成数组方法拦截,形成变异方法。

2.在vueRouter中使用此法,进行登录路由拦截。

下面我们来了解下AOP切面编程思想,比如下面这个例子。

# 1】before:修改原型使用闭包实现

//我们想要实现对say方法执行console之前之后进行一些操作。

function say(){
  //before---todo..
  console.log('说话')
  //after---todo..
}

此时我们就需要利用高阶函数,什么是高阶函数?高阶函数就是函数的参数是一个函数,或者返回值是一个函数。(回调函数是高阶函数的一种)

理一下思路,我们这里就可以修改方法原型,使用闭包来实现:

let newFn = say.before(function (){
  console.log('说话前')
})
//先say.before方法执行say中console之前需要执行的东西,返回一个say方法
//形成这种闭包之后,再执行当前newFn()方法即可调用say方法
newFn()

完整的实现过程:

function say(who,sth){
  console.log(who + '说话时' + sth)
}

Function.prototype.before=function (beforeFunc){
  let that=this//也可以使用下面箭头函数,因为箭头函数没有this,没有arguments,没有原型
  return function(){
    beforeFunc()
    that(...arguments)//如果是this的话,就需要看调用时的上下文,此时使用闭包,保留that变量在函数外也可以使用。
  }
}

let newFn=say.before(function (){
  console.log('说话前')
})
newFn('我','大笑');//如果是this的话,需要看调用时的上下文。


//打印出:
//说话前
//我说话时大笑

这里有一个知识点:...argumentsES6中的拓展运算符是使、通过apply(this,arguments)来实现的

//箭头函数回调的时候不能使用arguments,需要如下使用:

let fun =(...args)=>{//args数组展开形成括号里面的所有参数
  console.log(args)//[ 1, 2, 3, 4 ]
  console.log(...args)//1 2 3 4
}
fun(1,2,3,4)

注意:this只有在执行的时候才能确定,与词法作用域无关。

Function.prototype.before = function () {
    console.log(this);//[Function: say] 
  //这个this在函数执行的时候才能确定的,所以say.before()的时候才能确定。
}
function say(){
    
}
say.before()

# 2】Vue2.0你用 函数劫持 AOP 重写数组的方法

在调用push方法时,触发一次更新操作

[1,2,3].push(4)

首先拿到老的push方法

let oldPush=Array.prototype.push

写自己新的push方法

function push(...args){
  console.log('数据更新啦')
  oldPush.call(this,...args)
}
let arr=[1,2,3]
push.call(arr,4,5,6,7)
console.log(arr)

//打印出:
//数据更新啦
//[ 1, 2, 3, 4, 5, 6 ]

# 3】react setState 事务 等同于before/after

# 4】AOP面向切面编程思路图:

在这里插入图片描述

function perform(anyMethod, wrappers) {
  wrappers.forEach(wrapper => wrapper.initialize());
  anyMethod();
  wrappers.forEach(wrapper => wrapper.close());
}

perform(function () {
  console.log('say');
}, [{//wrapper1
  initialize: function () {
    console.log('wrapper1 beforeSay');
  },
  close: function () {
    console.log('wrapper1 close');
  }
}, {//wrapper2
  initialize: function () {
    console.log('wrapper2 beforeSay');
  },
  close: function () {
    console.log('wrapper2 close');
  }
}])

//wrapper1 beforeSay
//wrapper2 beforeSay
//say
//wrapper1 close
//wrapper2 close

增强写法:使用闭包让它不立即执行

function perform(anyMethod, wrappers) {
  return function () {
    wrappers.forEach(wrapper => wrapper.initialize());
    anyMethod();
    wrappers.forEach(wrapper => wrapper.close());
  }
}

let Func = perform(function () {
  console.log('say');
}, [{//wrapper1
  initialize: function () {
    console.log('wrapper1 beforeSay');
  },
  close: function () {
    console.log('wrapper1 close');
  }
}, {//wrapper2
  initialize: function () {
    console.log('wrapper2 beforeSay');
  },
  close: function () {
    console.log('wrapper2 close');
  }
}])

Func()

//wrapper1 beforeSay
//wrapper2 beforeSay
//say
//wrapper1 close
//wrapper2 close

# 5】使用generator和yield进行aop编程

原理:使用yield*进行再次迭代

function* generator() {
    yield* 'HEY'
}
for (let i of generator()) {//执行generator函数返回一个迭代器
    console.log(i)
}
//H
//E
//Y
let arr1 = [1, 2]
let arr2 = [10, 20]
function* chain(params) {
    yield 'before'
    for (let arr of params) {
        yield* arr
    }
    yield 'after'
}
for (let num of chain([arr1, arr2])) {
    console.log(num)
}
//before
//1
//2
//10
//20
//after

# 6】after:使用闭包来保存变量控制执行

js闭包变量回收问题

js的闭包变量回收

function after(times, callback) {//闭包
  return function () {//闭包将times变量一直存在栈中
    if (--times == 0) {
      callback();
    }
  }
}

let fn = after(3, function () {
  console.log('函数已执行');
})
fn()
fn()
fn()
//函数已执行

来做个练习:需求是在下面fun方法中3和5之间插入打印4

function fun(){
  fn()
  console.log('1')
  fn()
  console.log('2')
  fn()
  console.log('3')
  fn()
  console.log('5')
}
function after(times, callback) {//闭包
  return function () {//闭包将times变量一直存在栈中
    if (--times == 0) {
      callback();
    }
  }
}

let fn = after(4, function () {
  console.log('4');
})

fun()
Last Updated: 8/12/2020, 12:48:49 PM