前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >JS 完美的 AOP 编程

JS 完美的 AOP 编程

作者头像
villainhr
发布2018-07-03 15:07:48
9250
发布2018-07-03 15:07:48
举报
文章被收录于专栏:前端小吉米

看到 decorator这个词的时候,让我回想起了python中的decorator.而,当我看到 decorator中的 @的时候, 我tm确定,这尼玛不就是python吗? 但, too young too naive. es6中的decorator和python很相似,但却又非常的不一样.因为,在js中,decorator是不能用来装饰函数的.(因为有函数提升) so, decorator在js中是用来干嘛的呢?

神马是decorator

decorator是以一种近乎trick的方式,让你写更少的代码,完成更多的事情. 事实上,es 是借助了python中的decorator来,完善自身的语法树. 在es6中,decorator可以用来装饰类 || 类方法 || 类属性。 不过,在修饰类,方法,属性时,会有很大的不同.

decorator修饰类

使用decorator修饰类,他传入的参数代表的是还未实例化的类

代码语言:javascript
复制
// 使用decorator 装饰 类
// @param {class} target_class 相当于就是Target
function dec(target_class){
    target_class.prototype.demo=123
}
@dec
class Target{}

let target = new Target();
console.log(target.demo) // 123

ok, 这是比较简单的demo. 现在我们将decorator的数量增加,假设,现在想要在class上装饰两个decorator. 他们的执行顺序是怎样的呢?

多重decorator

实际让我们以一种通俗的方式, 来讲解一下。 ES6中的decorator和AOP编程中的before很类似. 先看一下运行的实例吧.

代码语言:javascript
复制
function dec_1(){
    console.log(1);
}
function dec_2(){
    console.log(2);
}
@dec_1
@dec_2
class Target{

}
// 执行结果
// 2
// 1
// 执行顺序为: dec_2 => dec_1

说白了, 这其实就是before的一个trick

代码语言:javascript
复制
function before (fn){
    return function(){
        fn()
        // do sth...
    }
}

这样, 你就可以在执行函数时,不断嵌套很多层的内容了. 或者更好的说,es6中的decorator是以一种由下到上的方式, 来执行装饰的内容的.

修饰属性

使用decorator修饰类中的属性, 其实和 Object.defineProperty的行为一模一样.可以对类中的属性访问做一下限制.(感觉和Proxy差多啊) 所以, 这实际上是限制了decorator不能用来作为参数处理的一个trick. 但作为属性限制,也是极好用的. 修饰属性的decorator接受三个参数.

代码语言:javascript
复制
/**
 * 装饰者
 * @param  {Object} 类为实例化的工厂类对象
 * @param  {String} name   修饰的属性名
 * @param  {Object} desc   描述对象
 * @return {descr}        返回一个新的描述对象
 */
function decorator(target,name,desc){
}

所以, 由于一开始设计decorator的初衷就是为了可拆卸化保护属性. 如果大家,对于属性的了解有疑问的话,可以参考我前面一篇的文章.es中的属性. 接下来,我们可以利用decorator来简单的写一个readonly的保护权限.

代码语言:javascript
复制
function readonly(target,key,desc){
    desc.writable=false;
    return desc;
}
class Target{
    @readonly
    demo=123;
}
new Target().demo=345; // 报错

所以, 针对于这一特性.我们还可以写很多其他的trick. 比如, 无法被for...in...遍历的属性, 无法使用delete删除的属性.

代码语言:javascript
复制
function noLoop(target,key,desc){
    desc.Enumerable=false;
    return desc;
}
function banDelete(target,key,desc){
    desc.configurable = false;
    return desc;
}

动态修改参数

有时候想要对函数传参做相关的属性和格式化设置,使用 decorator 的模式就非常方便了。前面已经知道,修饰一个函数可以获得的参数有:

代码语言:javascript
复制
/**
 * 装饰者
 * @param  {Object} 类为实例化的工厂类对象
 * @param  {String} name   修饰的属性名
 * @param  {Object} desc   描述对象
 * @return {descr}        返回一个新的描述对象
 */
function decorator(target,name,desc){
}

在这里对函数进行修饰,主要使用的是 desc 该对象。要对函数参数修改的基本步骤为:

  • 保留原始函数
  • 重新定义函数,使用 ...args 参数获取传参
  • 通过 apply 调用原始函数

现在,我们想对一个 Class Dog 的 setting 方法属性进行重新定义。原始 Dog 类为:

代码语言:javascript
复制
class Dog{
    constructor(name){
        this.setting(name)
    }

    setting(name){
        this._name = name;
    }
}

这里,我们额外增加一个 decoratorSetting 函数:

代码语言:javascript
复制
function decorateSetting(target, key, descriptor) {
  const method = descriptor.value; // 保存原始函数
  descriptor.value = (...args)=>{
  // 动态添加 villainhr- 的前缀
    args[0] = "villainhr-" + args[0]; // this is parameter (name)

    // 通过 apply 方法调用原始函数
    return method.apply(target, args);

  }
  return descriptor;
}

然后,使用 decorateSetting 修饰 Dog 类中的 setting 方法。

代码语言:javascript
复制
class Dog{
    constructor(name){
        this.setting(name)
    }
    @decorateSetting
    setting(name){
        this._name = name;
    }
}

然后运行一下,可以得到:

代码语言:javascript
复制
let pony = new Dog('pony'); // pony.name is villainhr-pony

到这里,也就成功的对函数进行了修饰。如果想线上运行,可以使用:babel-decorator online repr。

ok, descorator的最好用的部分,上面已经差不多参数清楚了。最后,我们在补充一下,使用 decorator 必要的 babel 转码配置。

babel 转码配置

直接贴代码了:

代码语言:javascript
复制
// 先使用 npm 下载额外的包
npm i --save-dev babel-plugin-transform-decorators-legacy

// webpack 配置文件
{
  test: /\.jsx?$/,
  loader: 'babel',
  query: {
    plugins: ['transform-decorators-legacy' ],
    presets: ['es2015', 'stage-0', 'react']
  }
}

// babelrc 配置文件
{
  "presets": ["es2015", "stage-0", "react"],
  "plugins": [
    ["transform-decorators-legacy"],
  ]
}

如果,大家有什么疑问. 或者有什么新的用法,可以直接楼下回复.

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2018-03-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端小吉米 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 神马是decorator
    • decorator修饰类
      • 多重decorator
    • 修饰属性
      • 动态修改参数
        • babel 转码配置
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档