JS 完美的 AOP 编程

看到 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修饰类,他传入的参数代表的是还未实例化的类

// 使用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很类似. 先看一下运行的实例吧.

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

function before (fn){
    return function(){
        fn()
        // do sth...
    }
}

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

修饰属性

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

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

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

function readonly(target,key,desc){
    desc.writable=false;
    return desc;
}
class Target{
    @readonly
    demo=123;
}
new Target().demo=345; // 报错

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

function noLoop(target,key,desc){
    desc.Enumerable=false;
    return desc;
}
function banDelete(target,key,desc){
    desc.configurable = false;
    return desc;
}

动态修改参数

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

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

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

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

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

class Dog{
    constructor(name){
        this.setting(name)
    }

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

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

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 方法。

class Dog{
    constructor(name){
        this.setting(name)
    }
    @decorateSetting
    setting(name){
        this._name = name;
    }
}

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

let pony = new Dog('pony'); // pony.name is villainhr-pony

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

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

babel 转码配置

直接贴代码了:

// 先使用 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"],
  ]
}

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

原文发布于微信公众号 - 前端小吉米(villainThr)

原文发表时间:2018-03-13

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏编程

Python读书笔记5

上期分享了Python相关的字符串应用,重点分享了转义字符。今天和大家分享和字符串相关的函数和应用。 一、字符串的合并! Python用“+”号可以连接两个文本...

1797
来自专栏小樱的经验随笔

队列的存储结构的实现(C/C++实现)

存档 1 #include "iostream.h" 2 #include "stdlib.h" 3 #define max 20 4 typedef ...

3336
来自专栏用户2442861的专栏

JAVA反射机制作用是什么

Java的反射机制是Java特性之一,反射机制是构建框架技术的基础所在。灵活掌握Java反射机制,对大家以后学习框架技术有很大的帮助。

1412
来自专栏进击的君君的前端之路

对象、原型

1584
来自专栏Debian社区

Python的闭包和装饰器

装饰器(Decorator)相对简单,咱们先介绍它:“装饰器的功能是将被装饰的函数当作参数传递给与装饰器对应的函数(名称相同的函数),并返回包装后的被装饰的函数...

744
来自专栏散尽浮华

Python内置函数

内置函数之一:lambda表达式 前面知识回顾: 三目运算: #普通条件语句 1: if 1 == 1: 2: name ='曹小贱' 3:...

1875
来自专栏我的技术专栏

Java锁机制(一)synchronized

1464
来自专栏数据结构与算法

BZOJ3585: mex(主席树)

Description   有一个长度为n的数组{a1,a2,...,an}。m次询问,每次询问一个区间内最小没有出现过的自然数。 Input   第一行n,m...

4069
来自专栏苦逼的码农

从jvm角度看懂类初始化、方法重载、重写。

类的声明周期可以分为7个阶段,但今天我们只讲初始化阶段。我们我觉得出来使用和卸载阶段外,初始化阶段是最贴近我们平时学的,也是笔试做题过程中最容易遇到的,假如你想...

862
来自专栏吴伟祥

logback高级特性使用 原

logback支持类似于占位符的变量替换功能,即如果输出的msg里面带有{}符号且括号中间不带其他字符,那么logback在构造LoggingEvent的时候,...

502

扫码关注云+社区