ES6之Decorator详解以及React中应用

439 阅读3分钟

这是我参与11月更文挑战的第20天,活动详情查看:2021最后一次更文挑战

背景

学习前端三个月了,准备刷刷面试题,总结总结,一天几道面试题,向大厂进军。

什么是Decorator

Decorator 也叫装饰器,是一种与class 相关的语法。重要的事情说三遍:与class相关。

装饰器字面意思就是装饰的作用,在程序中也是装饰,主要用来增强原有的类或者原有类方法的功能。

装饰器是一个函数,通用调用方式 @+函数名。它可以放在类或者类方法的定义前面,可以接受参数。

类装饰器

当对类本身进行装饰的时候,能够接受一个参数,即类本身。

我们看一下简单代码:

//装饰器
function testClassDecorator(target) {
   target.isTest = true;
 }

 //测试类
 @testClassDecorator
 class Test {
   private name = '';

   constructor(name) {
     this.name = name;
   }

   getName() {
     return this.name;
   }
 }
 
 //导出测试类
 export default Test;

    import Test from '@/es6Test/classTest';
    const a = new Test('张三');
    console.log('测试实例对象==', a.isTest, '==类对象==', Test.isTest);

输出结果: 测试实例对象== undefined ==类对象= true

上面代码中@testClassDecorator 就是一个装饰器。它修改的是类本身。它为Test类加上了静态属性isTest。testClassDecorator函数中的target就是Test类本身。

相当于:

//装饰器
function testClassDecorator(target) {
   target.isTest = true;
 }
 
  //测试类
 @testClassDecorator
 class Test { }
 
 Test = testClassDecorator(Test) || Test;

就是对一个类进行函数处理,函数的第一个参数是类本身。

注意: 装饰器是对类的行为改变,是代码编译的时候就发生改变了,不是在运行时。

装饰器接受参数

很简单,就是在装饰器函数外面再包装一层函数。

//接受参数
function testClassDecorator(age) {
   return function (target) {
     target.age = age;
   };
}

 @testClassDecorator(15)
 class Test {
   private name = '';

   constructor(name) {
     this.name = name;
   }

   getName() {
     return this.name;
   }
 }

 export default Test;

给实例对象增加属性

其实就是操作target.prototype

function testClassDecorator(age) {
   return function (target) {
     target.prototype.age = age;
   };
 }

 @testClassDecorator(15)
 class Test {
   private name = '';

   constructor(name) {
     this.name = name;
   }

   getName() {
     return this.name;
   }
 }

 export default Test;
    import Test from '@/es6Test/classTest';
    const a = new Test('张三');
    console.log('测试实例对象==', a.age, '==类对象==', Test.age);

输出结果:

测试实例对象== 15 ==类对象= undefined

给实例对象增加方法

//装饰器
function testClassDecorator(...list) {
    //给实例增加方法
   return function (target) {
     Object.assign(target.prototype, ...list);
   };
 }

 const Foo = {
   foo(num) {
     console.log('foo==', num);
   },
 };

 @testClassDecorator(Foo)
 class Test {
   private name = '';

   constructor(name) {
     this.name = name;
   }

   getName() {
     return this.name;
   }
 }

 export default Test;
    import Test from '@/es6Test/classTest';
    const a = new Test('张三');
    //调用增强的方法
    a.foo();

输出结果: foo== 2

React中具体应用

  1. 由于要经常使用react-redux,我们修改connect的写法为装饰器写法,使代码更优雅:

image.png

image.png 2. 我们经常使用ReactContext,经常需要写 Consumer。我们也可以把这个改为装饰器写法

image.png

image.png

类方法装饰器

装饰器不仅可以修饰类,还可以是修饰类的属性。

那修饰类方法的装饰器与修饰类的装饰器有什么区别呢??

我们先来看一个简单的案例:

    //方法装饰器
    function testClassDecorator(target, name, descriptor) {
       descriptor.writable = false;
      return descriptor;
   }

 class Test {
   private name = '';

   constructor(name) {
     this.name = name;
   }

   getName() {
     return this.name;
   }

   @testClassDecorator
   setName(name) {
     this.name = name;
   }
 }

 export default Test;

装饰器第一个参数是类的原型对象,上例是Test.prototype,装饰器的本意是要“装饰”类的实例,但是这个时候实例还没生成,所以只能去装饰原型(这不同于类的装饰,那种情况时target参数指的是类本身);第二个参数是所要装饰的属性名,第三个参数是该属性的描述对象。

带参数的类方法装饰器:

    //方法装饰器带参数
    function testClassDecorator(age) {
     return function(target, name, descriptor){
          descriptor.writable = false;
          return descriptor;
      }
   }

执行顺序

如果一个类或者方法有多个装饰器,顺序是先从外到内,然后执行从内到外

image.png

结语

一步一步慢慢来,踏踏实实把活干!