10-TypeScirpt-装饰器-混入
2020-10-05 本文已影响0人
低头看云
装饰器
1.什么是装饰器?
- Decorator 是 ES7 的一个新语法,目前仍处于提案中,
- 装饰器是一种特殊类型的声明,它能够被附加到类,方法, 访问器,属性或参数上
- 被添加到不同地方的装饰器有不同的名称和特点
- 附加到类上, 类装饰器
- 附加到方法上,方法装饰器
- 附加到访问器上,访问器装饰器
- 附加到属性上,属性装饰器
- 附加到参数上,参数装饰器
- 装饰器基本格式
2.1普通装饰器
2.2装饰器工厂
2.3装饰器组合
3.如何在TS中使用装饰器?
在TS中装饰器也是一项实验性的特性, 所以要使用装饰器需要手动打开相关配置
修改配置文件 experimentalDecorators
function test(target) {
console.log('test')
console.log('target', target)
}
// 如果一个函数返回一个回调函数, 如果这个函数作为装饰器来使用
// 那么这个函数就是装饰器工厂
function test1() {
console.log('test1 out')
return (target) => {
console.log('target', target)
console.log('test1 in')
}
}
function test2(target) {
console.log('test2')
}
function test3() {
console.log('test3 out')
return (target) => {
console.log('test3 in')
}
}
// 执行顺序
// 结合起来使用, 回想从上至下执行所有的装饰器工厂, 拿到所有真正的装饰器
// 然后再从下至上执行所有的装饰器
// test1 out / test3 out / test3 in / test2 / test1 in / test
@test
@test1()
@test2
@test3()
class Person {}
类装饰器
-
类装饰器在类声明之前绑定(紧靠着类声明)。
-
类装饰器可以用来监视,修改或替换类定义
-
在执行类装饰器函数的时候, 会把绑定的类作为其唯一的参数传递给装饰器
-
如果类装饰器返回一个新的类,它会新的类来替换原有类的定义
2.装饰器和装饰器工厂区别
时候可以传递自定义参数
function demo<T extends { new (...args: any[]) }>(target: T) {
return class extends target {
name: string = 'css'
age: number = 18
}
}
@demo
class Person1 {}
let p1 = new Person1()
console.log(p1)
defineProperty
-
Object.defineProperty()
- 可以直接在一个对象上定义一个新属性
- 或者修改一个对象的享有属性, 并返回此对象
-
定义一个新的属性
let obj = { age: 18 }
Object.defineProperty(obj, 'name', {
value: 'css',
})
console.log('obj', obj) // {age: 18, name: "css"}
- 修改原有属性
let obj1 = { age: 18 }
Object.defineProperty(obj1, 'age', {
value: 22,
})
console.log('obj1', obj1) // {age: 22}
- 修改属性配置 - 读写
let obj2 = { age: 18 }
Object.defineProperty(obj2, 'age', {
writable: false,
// writable: true,
})
obj2.age = 111
console.log('obj2', obj2) // {age: 18}
- 修改属性配置 - 迭代
let obj3 = { age: 14, name: 'js' }
Object.defineProperty(obj3, 'name', {
enumerable: false,
})
for (let key in obj3) {
console.log(key) // age
}
- 修改属性配置-配置
let obj4 = { age: 18, name: 'lnj' }
Object.defineProperty(obj4, 'name', {
enumerable: false,
configurable: false,
})
Object.defineProperty(obj4, 'name', {
enumerable: true,
configurable: false,
})
for (let key in obj4) {
console.log(key)
}
方法装饰器
-
方法装饰器写在在一个方法的声明之前(紧靠着方法声明)。
-
方法装饰器可以用来监视,修改或者替换方法定义。
-
方法装饰器表达式会在运行时当作函数被调用,传入下列3个参数:
- 对于静态方法而言就是当前的类, 对于实力方法而言就是当前的实例
- 被绑定方法的名字。
- 被绑定方法的属性描述符。
function test(
target: any,
propertKe: string,
descriptor: PropertyDescriptor
) {
console.log('descriptor', descriptor)
console.log('propertKe', propertKe)
console.log('target', target)
// configurable?: boolean;
// enumerable?: boolean;
// value?: any;
// writable?: boolean;
// descriptor.enumerable = false
descriptor.value = () => {
console.log('323232332')
}
}
class Person {
// @test
sayName(): void {
console.log('my name is css')
}
@test
sayAge(): void {
console.log('my age is 18')
}
// @test
static say(): void {
console.log('say hello world')
}
}
let p = new Person()
p.sayAge()
访问器装饰器
-
访问器装饰器声明在一个访问器的声明之前(紧靠着访问器声明)。
访问器装饰器应用于访问器的 属性描述符并且可以用来监视,修改或替换一个访问器的定义 -
访问器装饰器表达式会在运行时当作函数被调用,传入下列3个参数:
- 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
- 成员的名字。
- 成员的属性描述符。
-
注意:
TypeScript不允许同时装饰一个成员的get和set访问器。
取而代之的是,一个成员的所有装饰的必须应用在文档顺序的第一个访问器上
function test(
target: any,
propertyKey: string,
descriptor: PropertyDescriptor
) {
// console.log(target);
// console.log(propertyKey);
// console.log(descriptor);
descriptor.set = (value: string) => {
console.log('value', value)
target.myName = value
}
descriptor.get = (): string => {
return target.myName
}
}
class Person {
private _name: string
constructor(name: string) {
this._name = name
}
@test
get name(): string {
return this._name
}
set name(value: string) {
this._name = value
}
}
let p = new Person('css')
p.name = 'react'
console.log(p.name)
console.log('p', p)
参数装饰器
-
参数装饰器写在一个参数声明之前(紧靠着参数声明)。
-
参数装饰器表达式会在运行时当作函数被调用,传入下列3个参数:
- 对于静态成员来说是当前的类,对于实例成员是当前实例。
- 参数所在的方法名称。
- 参数在参数列表中的索引。
-
其它
属性装饰器,参数装饰器最常见的应用场景就是配合元数据(reflect-metadata),
在不改变原有结构的同时添加一些额外的信息
但是元数据目前也是在提案中, 也还没有纳入正式的标准
所以对于装饰器而言, 我们只需要了解即可,
因为提案中的所有内容将来都是有可能被修改的
因为提案中的所有内容目前都有兼容性问题
function test(target: any, proptyName: string, index: number) {
console.log(target)
console.log(proptyName)
console.log(index)
}
class Person {
say(age: number, @test name: string): void {}
}
属性装饰器
- 属性装饰器写在一个属性声明之前(紧靠着属性声明)
- 属性装饰器表达式会在运行时当作函数被调用,传入下列2个参数:
- 对于静态属性来说就是当前的类, 对于实例属性来说就是当前实例
- 成员的名字。
function test(target: any, proptyName: string) {
console.log(target)
console.log(proptyName)
target[proptyName] = 'css'
}
class Person {
@test
static age: number
// @test
name?: string
}
let p = new Person()
console.log(p)
console.log(Person.age)
混入
- 1.对象混入
let obj1 = {name:'lnj'};
let obj2 = {age:34};
Object.assign(obj1, obj2);
console.log(obj1);
console.log(obj2);
- 2.类混入
- 需求: 定义两个类, 将两个类的内容混入到一个新的类中
class Dog {
name: string = 'wc'
say(): void {
console.log('wang wang')
}
}
class Cat {
age: number = 3
run(): void {
console.log('run run')
}
}
// 注意点: 一次只能继承一个类
// class Animal extends Dog, Cat{
//
// }
class Animal implements Dog, Cat {
name: string
age: number
say: () => void
run: () => void
}
function myMixin(target: any, from: any[]) {
from.forEach((fromItem) => {
Object.getOwnPropertyNames(fromItem.prototype).forEach((name) => {
target.prototype[name] = fromItem.prototype[name]
})
})
}
myMixin(Animal, [Dog, Cat])
let a = new Animal()
console.log(a)
a.say()
a.run()
// console.log(a.name);
// console.log(a.age);