ECMAScript6-----装饰器

1.介绍

Decorator,即装饰器,从名字上很容易让我们联想到装饰者模式。简单来讲,装饰者模式就是一种在不改变原类和使用继承的情况下,动态地扩展对象功能的设计理论。

ES6中Decorator功能亦如此,其本质也不是什么高大上的结构,就是一个普通的函数,用于扩展类属性和类方法。

这里定义一个士兵,这时候他什么装备都没有。

class soldier{ 
}

定义一个得到AK装备的函数,即装饰器:

function strong(target){
    target.AK = true
}

使用该装饰器对士兵进行增强:

@strong
    class soldier{
}

这时候士兵就有武器了:

soldier.AK // true

上述代码虽然简单,但也能够清晰看到了使用Decorator两大优点:

  • 代码可读性变强了,装饰器命名相当于一个注释
  • 在不改变原有代码情况下,对原来功能进行扩展

2.装饰器的用法

Docorator修饰对象为下面两种:

  • 类的装饰
  • 类属性的装饰

2.1 准备环境

class语法是es6的标准,目前是不被浏览器完全支持的,我们需要一些工具将代码编译成es5。

  • typescript
  • rollup + babel 或者 webpack+ babel

我们采用webpack+babel的方式来处理。

npm init -y
npm install webpack webpack-dev-server webpack-cli html-webpack-plugin -D

安装所需的依赖@babel/core, @babel/preset-env, @babel/plugin-proposal-decorators,即Babel的核心库,环境预设以及装饰器插件:

npm install -D babel-loader @babel/core @babel/preset-env @babel/plugin-proposal-decorators

新建webpack.config.js:

const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
  mode: "development",
  entry: "./src/main.js",
  output: {
    filename: "[name].js",
    // 注意这个dist的路径设置成上一级
    path: path.resolve(__dirname, "./dist"),
    clean: true,
  },
  module: {
    rules: [
      {
        test: /\.m?js$/, //注意问号是可选的意思,表示前面一个字符选和不选
        exclude: /node_modules/, //注意:需在exclude中排除node_modules包
        use: {
          loader: "babel-loader",
          //配置对象
          options: {
            //   预设
            presets: ["@babel/preset-env"],
            plugins: [["@babel/plugin-proposal-decorators", { legacy: true }]],
          },
        },
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: "./public/index.html", //指向的html
      filename: "index.html", //被打包后的html文件名,
      inject: "body", //js打包后的生成位置
    }),
  ],
};

2.2 类的装饰

当对类本身进行装饰的时候,能够接受一个参数,即类本身。将装饰器行为进行分解,大家能够有个更深入的了解

@decorator
class A {}

// 等同于

class A {}
A = decorator(A) || A;

新建src/main.js

function testable(target) {
  target.isTestable = true;
}

@testable
class MyTestableClass {}

console.log(MyTestableClass.isTestable);

运行项目:

npx webpack serve

在这里插入图片描述

如果想要传递参数,可以在装饰器外层再封装一层函数:

function testable(isTestable) {
  return function (target) {
    target.isTestable = isTestable;
  };
}

@testable(true)
class MyTestableClass {}
console.log(MyTestableClass.isTestable); // true

@testable(false)
class MyClass {}
console.log(MyClass.isTestable); // false

2.3 类属性的装饰

当对类属性进行装饰的时候,能够接受三个参数:

  • 类的原型对象
  • 需要装饰的属性名
  • 装饰属性名的描述对象

首先定义一个readonly装饰器

function readonly(target, name, descriptor){
  descriptor.writable = false; // 将可写属性设为false
  return descriptor;
}

使用readonly装饰类的name方法

class Person {
  @readonly
  name() { return `${this.first} ${this.last}` }
}

相当于以下调用:

readonly(Person.prototype, 'name', descriptor);

如果一个方法有多个装饰器,就像洋葱一样,先从外到内进入,再由内到外执行。

function dec(id) {
  console.log("evaluated", id);
  return (target, property, descriptor) => console.log("executed", id);
}

class Example {
  @dec(1)
  @dec(2)
  @dec(3)
  method() {}
}
// evaluated 1
// evaluated 2
// evaluated 3
// executed 3
// executed 2
// executed 1

外层装饰器@dec(1)先进入,但是内层装饰器@dec(2)先执行。

注意:
装饰器不能用于修饰函数,因为函数存在变量声明情况。

var counter = 0;

var add = function () {
  counter++;
};

@add
function foo() {
}

编译阶段,变成下面

var counter;
var add;

@add
function foo() {
}

counter = 0;

add = function () {
  counter++;
};

意图是执行后counter等于1,但是实际上结果是counter等于0。

3.使用场景

基于Decorator强大的作用,我们能够完成各种场景的需求,下面简单列举几种:

使用react-redux的时候,如果写成下面这种形式,既不雅观也很麻烦

class MyReactComponent extends React.Component {}

export default connect(mapStateToProps, mapDispatchToProps)(MyReactComponent);

通过装饰器就变得简洁多了:

@connect(mapStateToProps, mapDispatchToProps)
export default class MyReactComponent extends React.Component {}

将mixins,也可以写成装饰器,让使用更为简洁了:

function mixins(...list) {
  return function (target) {
    Object.assign(target.prototype, ...list);
  };
}

// 使用
const Foo = {
  foo() { console.log('foo') }
};

@mixins(Foo)
class MyClass {}

let obj = new MyClass();
obj.foo() // "foo"
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

太阳与星辰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值