属性装饰器应用于类的属性,可以修改属性的行为。属性装饰器接收两个参数:目标对象和属性名。
属性装饰器是一个函数,接收两个参数。
function log(target: any, propertyKey: string) {
console.log(`装饰属性: ${propertyKey}`)
}
class User {
@log
name: string = "张三"
}
属性装饰器的两个参数:
function inspect(target: any, propertyKey: string) {
console.log("target:", target === User.prototype)
console.log("propertyKey:", propertyKey)
}
class User {
@inspect
name: string = "张三"
}
属性装饰器可以将属性设为只读。
function readonly(target: any, propertyKey: string) {
Object.defineProperty(target, propertyKey, {
writable: false,
configurable: true
})
}
class User {
@readonly
name: string = "张三"
}
const user = new User()
user.name = "李四"
属性装饰器可以验证属性值。
function validateEmail(target: any, propertyKey: string) {
let value: string
Object.defineProperty(target, propertyKey, {
get: () => value,
set: (newValue: string) => {
if (!newValue.includes("@")) {
throw new Error("邮箱格式不正确")
}
value = newValue
},
configurable: true
})
}
class User {
@validateEmail
email: string = "test@example.com"
}
const user = new User()
user.email = "invalid"
属性装饰器可以格式化属性值。
function uppercase(target: any, propertyKey: string) {
let value: string
Object.defineProperty(target, propertyKey, {
get: () => value,
set: (newValue: string) => {
value = newValue.toUpperCase()
},
configurable: true
})
}
class User {
@uppercase
name: string = ""
constructor(name: string) {
this.name = name
}
}
const user = new User("zhang san")
console.log(user.name)
属性装饰器可以设置默认值。
function defaultValue(value: any) {
return function (target: any, propertyKey: string) {
Object.defineProperty(target, propertyKey, {
value: value,
writable: true,
configurable: true
})
}
}
class User {
@defaultValue("未命名")
name: string
@defaultValue(0)
age: number
}
const user = new User()
console.log(user.name)
console.log(user.age)
属性装饰器可以限制属性值的范围。
function range(min: number, max: number) {
return function (target: any, propertyKey: string) {
let value: number
Object.defineProperty(target, propertyKey, {
get: () => value,
set: (newValue: number) => {
if (newValue < min || newValue > max) {
throw new Error(`${propertyKey} 必须在 ${min} 到 ${max} 之间`)
}
value = newValue
},
configurable: true
})
}
}
class User {
@range(0, 150)
age: number = 25
}
const user = new User()
user.age = 30
console.log(user.age)
user.age = 200
属性装饰器可以追踪属性变化。
function track(target: any, propertyKey: string) {
let value: any
Object.defineProperty(target, propertyKey, {
get: () => value,
set: (newValue: any) => {
console.log(`${propertyKey} 从 ${value} 变为 ${newValue}`)
value = newValue
},
configurable: true
})
}
class User {
@track
name: string = ""
@track
age: number = 0
}
const user = new User()
user.name = "张三"
user.age = 25
属性装饰器可以用于表单验证。
interface Validator {
validate(value: any): boolean
message: string
}
const validators = new Map<string, Validator[]>()
function addValidator(validator: Validator) {
return function (target: any, propertyKey: string) {
const key = `${target.constructor.name}.${propertyKey}`
if (!validators.has(key)) {
validators.set(key, [])
}
validators.get(key)!.push(validator)
}
}
function required(target: any, propertyKey: string) {
addValidator({
validate: (value) => value !== undefined && value !== null && value !== "",
message: `${propertyKey} 是必填项`
})(target, propertyKey)
}
function minLength(length: number) {
return function (target: any, propertyKey: string) {
addValidator({
validate: (value) => value.length >= length,
message: `${propertyKey} 长度至少为 ${length}`
})(target, propertyKey)
}
}
function validate(target: any): boolean {
let isValid = true
for (const [key, validatorList] of validators) {
const [className, property] = key.split(".")
if (target.constructor.name === className) {
const value = (target as any)[property]
for (const validator of validatorList) {
if (!validator.validate(value)) {
console.log(validator.message)
isValid = false
}
}
}
}
return isValid
}
class UserForm {
@required
@minLength(2)
name: string = ""
@required
email: string = ""
}
const form = new UserForm()
form.name = "张"
form.email = ""
validate(form)
属性装饰器应用于类的属性,接收目标对象和属性名两个参数。属性装饰器可以实现只读属性、属性验证、格式化、默认值、范围限制等功能。属性装饰器常用于表单验证、数据绑定等场景。属性装饰器不能直接访问属性值,需要使用 Object.defineProperty 来修改属性行为。