大家好,都吃晚饭了吗?我是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方法是只读的,也是无法枚举的。