@instance
method2 = () => {}
// 静态成员
@static
static method3 () {}
@static
static method4 = () => {}
}
function instance(target) {
console.log(target.constructor === Model)
}
function static(target) {
console.log(target === Model)
}
函数,访问器,和属性装饰器三者之间的区别
函数
首先是函数,函数装饰器的返回值会默认作为属性的value描述符存在,如果返回值为undefined则会忽略,使用之前的descriptor引用作为函数的描述符。
所以针对我们最开始的统计耗时的逻辑可以这么来做:
class Model {
@log1
getData1() {}
@log2
getData2() {}
}
// 方案一,返回新的value描述符
function log1(tag, name, descriptor) {
return {
...descriptor,
value(...args) {
let start = new Date().valueOf()
try {
return descriptor.value.apply(this, args)
} finally {
let end = new Date().valueOf()
console.log(`start: ${start} end: ${end} consume: ${end - start}`)
}
}
}
}
// 方案二、修改现有描述符
function log2(tag, name, descriptor) {
let func = descriptor.value // 先获取之前的函数
// 修改对应的value
descriptor.value = function (...args) {
let start = new Date().valueOf()
try {
return func.apply(this, args)
} finally {
let end = new Date().valueOf()
console.log(`start: ${start} end: ${end} consume: ${end - start}`)
}
}
}
访问器
访问器就是添加有get、set前缀的函数,用于控制属性的赋值及取值操作,在使用上与函数没有什么区别,甚至在返回值的处理上也没有什么区别。
只不过我们需要按照规定设置对应的get或者set描述符罢了:
class Modal {
_name = 'Niko'
@prefix
get name() { return this._name }
}
function prefix(target, name, descriptor) {
return {
...descriptor,
get () {
return `wrap_${this._name}`
}
}
}
console.log(new Modal().name) // wrap_Niko
属性
对于属性的装饰器,是没有返回descriptor的,并且装饰器函数的返回值也会被忽略掉,如果我们想要修改某一个静态属性,则需要自己获取descriptor:
class Modal {
@prefix
static name1 = 'Niko'
}
function prefix(target, name) {
let descriptor = Object.getOwnPropertyDescriptor(target, name)
Object.defineProperty(target, name, {
...descriptor,
value: `wrap_${descriptor.value}`
})
}
console.log(Modal.name1) // wrap_Niko
对于一个实例的属性,则没有直接修改的方案,不过我们可以结合着一些其他装饰器来曲线救国。
比如,我们有一个类,会传入姓名和年龄作为初始化的参数,然后我们要针对这两个参数设置对应的格式校验:
const validateConf = {} // 存储校验信息
@validator
class Person {
@validate('string')
name
@validate('number')
age
constructor(name, age) {
this.name = name
this.age = age
}
}
function validator(constructor) {
return class extends constructor {
constructor(...args) {
super(...args)
// 遍历所有的校验信息进行验证
for (let [key, type] of Object.entries(validateConf)) {
if (typeof this[key] !== type) throw new Error(`${key} must be ${type}`)
}
}
}
}
function validate(type) {
return function (target, name, descriptor) {
// 向全局对象中传入要校验的属性名及类型
validateConf[name] = type
}
}
new Person('Niko', '18') // throw new error: [age must be number]
首先,在类上边添加装饰器@validator,然后在需要校验的两个参数上添加@validate装饰器,两个装饰器用来向一个全局对象传入信息,来记录哪些属性是需要进行