资讯专栏INFORMATION COLUMN

面向切面编程与装饰器

lunaticf / 2414人阅读

摘要:面向切面编程嗯,百度百科一下为的缩写,意为面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。

面向切面编程

~~~~ 嗯,百度百科一下 ~~~~

AOP 为 Aspect Oriented Programming 的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP 是 OOP 的延续,是软件开发中的一个热点,也是 Spring 框架中的一个重要内容,是函数式编程的一种衍生范型。利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

作为前端的小萌理解为,就是将除主流程外的其他业务剥离出来,然后从侧面切入。对原功能无侵入式的修改。

话不多说,来个网上的经典例子:

业务逻辑:

主业务:修改数据库

切入点 1:修改前打印日志

切入点 2:修改后打印日志

Function.prototype.before = function(beforeFunc) {
  let that = this
  return function() {
    beforeFunc.apply(this, arguments)
    return that.apply(this, arguments)
  }
}

Function.prototype.after = function(afterFunc) {
  let that = this
  return function() {
    let ret = that.apply(this, arguments)
    afterFunc.apply(this, arguments)
    return ret
  }
}

function updateDb() {
  console.log(`update db`)
}
function beforeUpdateDb() {
  console.log(`before update db`)
}
function afterUpdateDb() {
  console.log(`updated db`)
}
updateDb = updateDb.before(beforeUpdateDb).after(afterUpdateDb)
updateDb()

原理:其实就是将主业务updateDb包裹2次,返回一个新的方法。新方法会在原方法调用前后调用切入方法,避免在主方法上直接改动。

这样的可读性和代码维护性是不是很差,下面用装饰器看看如何实现切面编程,

装饰器 decorator

装饰器是ES7现代游览器并不兼容,需要babel转译,插件(babel-plugin-transform-decorators-legacy)

装饰器只能作用于类本身、类的方法或属性、访问操作符

修饰器“@”为标识符

1 对类的装饰
@create
class Apes {
 
}

// 修饰类本身
function create(className) {
  className.prototype.create = function() {
    console.log("制造工具")
  }
  return descriptor
}

let apes1 = new Apes()
apes1.create() 
// 制造工具

对类本身修饰:create(className)。装饰器本质就是编译时执行的函数。

要修饰子类,通过要className.prototype修饰子类。

2 对类的方法修饰
class Apes {
  @eatMore
  eat() {
    console.log("吃水果")
  }
}
// 修饰方法
function eatMore(className, propName, descriptor) {
  //console.log(descriptor)
  let value = descriptor.value
  descriptor.value = function() {
    console.log("吃土")
    value()
  }
  return descriptor
}

let apes1 = new Apes()
apes1.eat()

对类的方法装饰 eatMore(className, propName, descriptor)

className - 被修饰的类
propName - 被修饰的属性名
descriptor - 该属性的描述对象

通过descriptor属性描述符看出 依赖于ES5的Object.defineProperty

复习一下Object.defineProperty

value:属性的值,默认为undefined

writable:能否修改属性的值,默认值为true

enumerable:能否通过for-in循环返回属性。默认为ture

configurable:能否通过delete删除属性从而重新定义属性,能否修改属性的特性,能否把属性修改为访问器属性,默认为true.

上面对类的方法修饰 实际是通过 descriptor.value 拿到其方法,在进行包装返回

同时也可以直接修改descriptor上的其他属性或者返回一个新的descriptor

3. 针对 访问操作符的装饰
class test {
  //@nonenumerable
  get kidCount() {
    return 111
  }
}

function disWirte(target, name, descriptor) {
  descriptor.writable = false
  return descriptor
}
let p = new test()
console.log(p.kidCount)
p.kidCount = 222n descriptor;

// 抛出异常
// TypeError: Cannot set property kidCount of # which has only a getter
4. 修饰传参
class Apes {
  @say("可以说汉语了")
  say() {
    console.log("厵爨癵驫寶麣纞虋讟钃鸜麷鞻韽顟顠饙饙騳騱龗鱻爩麤")
  }
}

// 修饰方法并传递参数
function say(str) {
  return function(className, propName, descriptor) {
    descriptor.value = function() {
      console.log(str)
    }
    return descriptor
  }
}

通过柯里化的方式传递参数

应用 1. 应用在斐波那契数列计算中
const memory = () => {
  const cache = {} //缓存池
  return (target, name, descriptor) => {
    // 原方法
    const method = descriptor.value
    // 包裹原方法
    descriptor.value = function(key) {
      if (cache[key]) {
        return cache[key]
      }
      const ret = method.apply(target, [key])
      cache[key] = ret
      return ret
    }
    return descriptor
  }
}

let count = 0

class Test {
  @memory()
  fb(n) {
    count++
    if (n === 1) return 1
    if (n === 2) return 1
    return this.fb(n - 1) + this.fb(n - 2)
  }
}

const t = new Test()
console.log(t.fb(10))
console.log(count)
2. 访问操作符-set 上作类型检查
class test {
  constructor() {
    this.a = 1
  }
  get a() {
    return this.a
  }
  @check("number")
  set a(v) {
    return v
  }
}

function check(type) {
  return function(target, prop, descriptor) {
    let v = descriptor.value
    return {
      enumerable: true,
      configurable: true,
      get: function() {
        return v
      },
      set: function(c) {
        var curType = typeCheck(c)
        if (curType !== type) {
          throw `${prop}必须为${type}类型`
        }
        v = c
      }
    }
  }
}

function typeCheck(c) {
  if (c === undefined) {
    return undefined
  }
  if (c === null) {
    return null
  }
  let type = typeof c
  if (type === "object") {
    return c.constructor == Array ? "array" : "object"
  }
  return type
}

let t = new test(11)
t.a = 2
console.log(t.a) // 2

t.a = []
console.log(t.a)
// throw a必须为number类型

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/106576.html

相关文章

  • 前端装饰,AOP的使用

    摘要:中的装饰在前端编程中,我们可以采用装饰器,来实现编程。装饰器使用我们先建立一个简单的类,这个类的作用,就是在执行的时候,打印出。至此,一个简单的装饰器范例已经完成。 什么是装饰器? 了解AOP 在学习js中的装饰器之间,我们需要了解AOP(面向切面编程)编程思想。 AOP是一种可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。AOP实际是Go...

    lewinlee 评论0 收藏0
  • 面向复杂应用,Node.js中的IoC容 -- Rockerjs/core

    摘要:项目地址项目主页基于和注解的轻量级容器,提供了依赖注入面向切面编程及异常处理等功能。可在任意工程中引入,是一个框架无关的容器。模块不依赖于任何框架,并与现有框架库类等保持兼容。 Rockerjs Core 项目地址 项目主页 基于 TypeScript 和注解的轻量级IoC容器,提供了依赖注入、面向切面编程及异常处理等功能。Rockerjs Core可在任意工程中引入,是一个框架无...

    jasperyang 评论0 收藏0
  • 面向复杂应用,Node.js中的IoC容 -- Rockerjs/core

    摘要:项目地址项目主页基于和注解的轻量级容器,提供了依赖注入面向切面编程及异常处理等功能。可在任意工程中引入,是一个框架无关的容器。模块不依赖于任何框架,并与现有框架库类等保持兼容。 Rockerjs Core 项目地址 项目主页 基于 TypeScript 和注解的轻量级IoC容器,提供了依赖注入、面向切面编程及异常处理等功能。Rockerjs Core可在任意工程中引入,是一个框架无...

    Kosmos 评论0 收藏0
  • 5 分钟即可掌握的 JavaScript 装饰者模式 AOP

    摘要:下装饰者的实现了解了装饰者模式和的概念之后,我们写一段能够兼容的代码来实现装饰者模式原函数拍照片定义函数装饰函数加滤镜用装饰函数装饰原函数这样我们就实现了抽离拍照与滤镜逻辑,如果以后需要自动上传功能,也可以通过函数来添加。 showImg(https://segmentfault.com/img/bVbueyz?w=852&h=356); 什么是装饰者模式 当我们拍了一张照片准备发朋友...

    chunquedong 评论0 收藏0
  • es6/es7之Decorator装饰

    摘要:装饰器顾名思义就是装饰某种东西的方法,可以用来装饰属性变量函数类实例方法本质上是个函数。以符开头,函数名称自拟。爱吃苹果装饰器装饰类爱吃苹果结果是这个类本身就可以通过修改类的属性增加属性被装饰的对象可以使用多个装饰器。 @Decorator 装饰器是es7的语法,这个方法对于面向切面编程有了更好的诠释,在一些情境中可以使用,比如路人A的代码实现了一需求,路人B希望用A的方法来实现一个新...

    yanest 评论0 收藏0

发表评论

0条评论

lunaticf

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<