类中的装饰器

装饰器 装饰模式

在执行类之前可以进行包装

1
2
3
4
5
6
7
8
9
10
11
12
@type
class Animal{

}
// type(Animal)
// 类似于这样 可以当做是它的语法糖

// 默认会调用这个type并且把Animal传过来
function type(Constructor){
console.log(Constructor); // webpack环境运行
}
// class Animal {}
1
2
3
4
5
6
7
8
9
10
11
12
@type1
@type2
class Animal{

}
function type1(Constructor){
console.log(1);
}
function type2(Constructor){
console.log(2);
}
// 打印 2 1 执行顺序 先走最近的

对类进行扩展 也可以传参

1
2
3
4
5
6
7
8
9
10
11
@type1('哺乳1') 
@type2('哺乳2')
class Animal{

}
function type1(Constructor){
Constructor.type1 = '哺乳类1';
}
function type2(Constructor){
Constructor.type2 = '哺乳类2';
}

装饰器必须是个函数 只能修饰类 (类中的属性 类中的方法):参数分别是 类的原型 装饰的key 和key对应的属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@type1('哺乳1') // 之前type1是个函数 现在type1执行之后的结果是个函数
@type2('哺乳2') // 这里就是把Animal传给了这个函数的返回值 所以line8放置参数Constructor
class Animal{

}
function type1(type1){
console.log('t1');
return function(Constructor){
console.log('inner t1');
Constructor.type1 = type1;
}
}
function type2(type2){
console.log('t2');
return function(Constructor){
console.log('inner t2');
Constructor.type1 = type2;
}
}
// 执行顺序 t1 => t2 => inner t2 => inner t1

类中的属性
1
2
3
4
5
6
class Circle{
PI = 3.14; // 这样写表示实例上的属性
}
let c = new Circle();
c.PI = 3.15;
console.log(c); // 3.15

现在我希望不能更改这个值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Circle{
@readonly PI = 3.14;
a(){}
}
function readonly(CirclePropertype, key, descriptor){
console.log(CirclePropertype, key, descriptor);
// 这里Circle还没执行完 才执行到line2 所以line8这样写调取不到 用定时器来执行下
//console.log(CirclePropertype === Circle.prototype);
setTimeout(() => {
console.log(CirclePropertype === Circle.prototype); // true
}, );
console.log(descriptor.initializer()); // 3.14 调取descriptor中的initializer返回的就是value的值
// 现在希望是只读的
descriptor.writable = false;
}
let c = new Circle();
c.PI = 3.15;
console.log(c); // PI 3.14
类中的方法
1
2
3
4
5
6
7
class Circle{
say(){
console.log('说话');
}
}
let c = new Circle();
c.say(); // 说话

现在希望在说话前做些其他的事情

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Circle{
@before
say(){
console.log('说话');
}
}
function before(CirclePropertype, key, descriptor){
console.log(CirclePropertype, key, descriptor);
// 通过打印可以看到 这里 descriptor.value 指的是say方法
let oldSay = descriptor.value; // 函数劫持 这个是原有的方法
descriptor.value = function(){ // 将函数原有的逻辑进行包装
console.log("todo");
oldSay();
}
}
let c = new Circle();
c.say(); // todo 说话

装饰器 实验型语法 目前node不支持

拓展 mixin 混合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 常见用法
let obj = {
name: '加菲',
age: 18
}
@mixin(obj)
class School{

}
function mixin(obj){
return function(Constructor){ // Constructor 就是school
Object.assign(Constructor.prototype, obj);
}
}
let school = new School;
console.log(school.name, school.age); // 加菲 18
重写原型方法

希望调用数组的方法 实现视图的更新

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const update = ()=>{
console.log('更新')
}
let arr = [];
let oldProto = Array.prototype; // 保留数组原有的原型
let proto = Object.create(oldProto); // 创建了一个新proto
['push','unshift','shift'].forEach(method=>{
proto[method] = function(...args){
update();
oldProto[method].call(this,...args);
}
})

function observer(obj){ // 只将我们传入的数组中的方法重写
if(Array.isArray(obj)){
obj.__proto__ = proto
}
}
observer(arr);
arr.push(1);
console.log(arr);
[].push(4,5,6)