babel装饰器

2,290 阅读2分钟

今天学习koa过程中用到了babel装饰器,因为对这部分不熟悉,所以做个笔记。 decorator是一个语法糖,可以用来修饰类/方法、对象的描述符。

修饰类

如果装饰类的decorator函数返回值为空,那么yourClassName就是装饰后的类;如果不为空,那么yourClassName就是返回值。

例如: 类B在装饰后,变量B的值就是18。

有必要时,我们可以返回新的类来替换原来的类,但多数情况下我们还是不需要返回值的。

@sayHello
class A {}
function sayHello (target) {
    // target就是我们正在装饰的类A
    target.prototype.sayHello = () => console.log('hello')
}
// 或者
@setAge(18)
class B {}

function setAge (n) {
 return (target) => {
 	return target.age = n
 }
}

/*  对类A的修饰

var _dec, _class;

let A = (_dec = setAge(18), _dec(_class = class A {}) || _class);

function setAge(n) {
  return target => {
    return target.age = n;
  };
}

*/

修饰对象的描述符

修饰方法是对该方法的修饰符对象做修饰

descriptor对象:{ configurable: true, enumerable: false, writable: true, value: fn }

class MyClass {
    @readOnly
    print () {}
}
// 返回 访问器属性
function readOnly (target, key, descriptor) {
    descriptor.writable = false
    return descriptor
}
// 或者 返回数据属性
function Decorator(type){
    return function (target, name, descriptor){
        let v = descriptor.initializer && descriptor.initializer.call(this);
        return {
            enumerable: true,
            configurable: true,
            get: function() {
                return v;
            },
            set: function(c) {
                v = c;
            }
        }
    }
}

多个修饰器

在此之前我们可以了解

  1. @setAge(18), setAge函数会执行,返回_decorator函数(参数为 target、key、descriptor)
  2. @myhobby, 函数本身就是包装器函数
class A {
    @decorator(11)
    @decorator2(22)
    @decorator3
    mymethod() {}
}

function decorator (n) {
    return function desc1(target, key, descriptor) {
        
    }
}

function decorator2 (n) {
    return function desc2 (target, key, descriptor) {
        
    }
}
function decorator3 (t, k, descriptor) => {
    
}

转化后的代码

// 1. 获取包装器函数
var _dec1 = decorator(11), _dec2 = decorator2(22);
var _class = class A { mymethod() {} }


_applyDecoratedDescriptor(_class.prototype, 'mymethod', [_dec1, _dec2, decorator3], Object.getOwnPropertyDescriptor(_class.prototype, 'mymethod'), _class.prototype);


function _applyDecoratedDescriptor( target, property, decorators,
descriptor, context) {
  // 1. desc: 对方法修饰符做浅拷贝
  var desc = {}
  Object.keys(descriptor).forEach(function(key) {
    desc[key] = descriptor[key]
  })
  desc.enumerable = !!desc.enumerable
  desc.configurable = !!desc.configurable
  if ('value' in desc || desc.initializer) {
    desc.writable = true
  }
  // 2. decorators对象反序,然后执行里面的函数
  // 注意到在这个过程中:
  // target是全局的,对target修改可以在下一个_desc获得
  // _desc返回值是修饰符,不返回则取上一个_desc,默认是desc。
  // 执行得到的就是该方法最终的修饰符对象
  desc = decorators.slice().reverse()
    .reduce(function(desc, decorator) {
      return decorator(target, property, desc) || desc
    }, desc)
  if (context && desc.initializer !== void 0) {
    desc.value = desc.initializer ? desc.initializer.call(context) : void 0
    desc.initializer = undefined
  }
  if (desc.initializer === void 0) {
    Object.defineProperty(target, property, desc)
    desc = null
  }
  return desc
}

附上koa配置的.babelrc文件


{
    "presets": [
      [
        "@babel/preset-env",
        {
          "targets": {
            "node": "current"
          }
        }
      ]
    ],
    "plugins": [
      ["@babel/plugin-proposal-decorators", { "legacy": true }],
      ["@babel/plugin-proposal-class-properties", { "loose" : true }]
    ]
}