js ES7装饰器Decorator - Kaiqisan

大家好,都吃晚饭了吗?我是Kaiqisan,是一个已经走出社恐的一般生徒,
第一次了解装饰器还是在使用reace-redux的时候,在听闻了它的大名之后我第二天就去学习了它,卧槽好难!

我虽然讲得不好,但一定通俗
下面贴一些专业的介绍装饰器的网站
阮一峰ES6
redream

好了开始讲了~

装饰器是一个和ES6语法 class一起配合使用的一个方法,它的提案经过了大幅修改,目前还没有定案,目前所有主流的浏览器都无法兼容这个语法(

2021-1-2更新因为它是TS语法

),需要babel来解释 (babel7.x以上使用 @babel/plugin-proposal-decorators babel7.x以下的使用 babel-plugin-transform-decorators-legacy

装饰器有如下用法

装饰类

@add
class Demo {
  // ...
}

function add(target) {
  target.params = true;
}

console.log(Demo.params); // 输出为true

在上面的代码中,修饰器为类添加了一个静态参数 params可以使用console.log(Demo.prototype)验证
上面的add函数直接执行了 ,上面的代码相当于,add函数的参数target传入的是下面的类(所以装饰器若想要传入相应的类的话,在编写的时候需要紧贴这个类,写在类上面)

class Demo  {}
Demo  = add(Demo) || Demo;

装饰器也可以传参来进行‘定制’

function add(flag) {
  return function(target) {
    target.params = flag;
  }
}

@add(true)
class Demo{}
console.log(Demo.params); // true

如果要传参的话就必须使装饰器函数返回一个函数,且这个返回的函数的第一个参数(target)是这个类,它的固定写法就是这样,不可更改

它相当于

class Demo  {}
Demo  = add(flag)(Demo) || Demo;

括号代表执行函数,前面的括号执行外层的function,后面的括号执行

如果不想添加静态的数据而是直接添加实例属性的话就需要如下做法

function add(flag) {
    return function (target) {
        target.prototype.params = flag // 这么写
    }
}

@add(true)
class Demo {
}

let a = new Demo()
console.log(a.params) // true

添加一个函数

function add(flag) {
    return function (target) {
        target.prototype.doFun1 = flag // 这么写
    }
}

let fun1 = function () {
    console.log('ok')
}

@add(fun1)
class Demo {
}

let a = new Demo()
a.doFun1() // ok

如果传入多个参数

function add(...flag) {
    return function (target) {
        Object.assign(target.prototype, ...flag) // 使用扩展运算符
    }
}

let fun1 = {
    app: function() {
        console.log('ok')
    },
    name: 'kaiqisan',
    uid: 10001
}

@add(fun1)
class Demo {
}

let a = new Demo()
// a.info.app() // ok
console.log(a.name, a.uid, a.app()); // kaiqisan 10001 'ok'

在实际操作中这个装饰器总是与 @connect联系在一起,有兴趣的可以看看下面的文章
react-redux @connect

装饰类的属性

class Demo {
  @readonly
  name() { return 'this.name' }
}
function readonly(target, name, descriptor){
  // descriptor对象原来的值如下
  // {
  //   value: specifiedFunction,
  //   enumerable: false,
  //   configurable: true,
  //   writable: true
  // };
  //
  descriptor.writable = false;
  
  return descriptor;
}

这类似于

Object.defineProperty(Demo.prototype, 'name', descriptor);

这个方法必须返回一个对象descriptor,这是在我们实例化这个类的时候会执行装饰器的函数,它有三个参数,

  • 第一个参数target是类的原型对象,即Demo.prototype.
  • 第二个参数是要装饰的属性名,在上面的例子里面指的是name这个函数名
  • 第三个参数是该属性的描述对象,它里面有四个值
    • value: 属性值
    • enumerable: 是否可被枚举
    • configurable:是否可被delete方法删去
    • writable:是否可修改其中的值

这一部分知识详情见 Object.defineProperty的知识

所以上面的装饰器进行的操作就是把name这个成员设置为不可修改,当我们尝试修改的时候报错了!
在这里插入图片描述

所以我们对类中的某属性进行基础的配置的时候(是否可枚举,删除,修改),会用到descriptor

除了使用descriptor修改属性的基础type,我们也可以在这个函数内再装修这个类

function readonly(target, name, descriptor) {
    console.log(target, name, descriptor);
    target.uid = 10001
    descriptor.writable = false
    descriptor.value = 'app'
    return descriptor
}

总结

所以所,装饰器这个东西对于我们在语言底层调试非常有用,对于其命名,需要规范,要简洁规范易懂。

class Person {
  @readonly
  @nonenumerable
  name() { return `${this.first} ${this.last}` }
}

咱一眼就可以看出这个类中的name方法是只读的,也是无法枚举的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kaiqisan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值