es6装饰器

babel配置

开始之前,需要准备好编译所需的配置环境,安装对应的babel依赖包。

首先,控制台执行 npm init -y 在根目录下生成 package.json 文件。

接着下载babel所需的依赖包:

npm install @babel/cli @babel/core @babel/preset-env -D

babel提供了编译装饰器和类所需的两个插件:

接着下载这两个插件:

npm install @babel/plugin-proposal-decorators @babel/plugin-proposal-class-properties -D

下载完成后,package.json里面会有对应的版本依赖:

"devDependencies": {
    "@babel/cli": "^7.11.6",
    "@babel/core": "^7.11.6",
    "@babel/preset-env": "^7.11.5",
    "@babel/plugin-proposal-decorators": "^7.10.5",
    "@babel/plugin-proposal-class-properties": "^7.10.4"
}

接着编写babel配置脚本,新建.babelrc文件,输入以下配置:

{
    "presets": ["@babel/preset-env"],
    "plugins": [
        ["@babel/plugin-proposal-decorators",{"legacy": true}],
        ["@babel/plugin-proposal-class-properties",{"loose": true}]
    ]
}

这里没有使用webpack,直接用babel自带的命令编译我们的文件,在package.json中新增start命令:

"scripts": {
    "start": "npx babel app.js -o index.js -w"
}

-w 表示监听文件变化,文件发生改变就会执行编译,-o 表示生产后的文件名

类装饰器

新建app.js,控制台执行npm start命令就可以开始我们的测试了,下面的代码都可以在app.js中修改。

新增方法

通过装饰器给构造函数新增原型方法:

function flag(constructor) {
  constructor.prototype.getName = function () {
    return this.name;
  };
}

@flag
class Animal {
  constructor(name) {
    this.name = name;
  }
  sayHi() {
    // this.getName 通过flag新增
    console.log(this.getName() + " say:'hi'");
  }
}

let monkey = new Animal("monkey");

monkey.sayHi();

传参

装饰器也可以传参,有点面向切面编程(AOP)的味道了,具体方法如下:

function flag(params) {
  console.log(params);
  return (constructor) => {
    constructor.prototype.getName = function () {
      return this.name;
    };
  };
}

@flag({ specie: "mammal" })
class Animal {
  constructor(name) {
    this.name = name;
  }

  sayHi() {
    console.log(this.getName() + " say:'hi'");
  }
}

方法装饰器

函数方法也能添加装饰器,而且更能体现切片编程的思想,效果如下:

function flag(constructor) {
  constructor.prototype.getName = function () {
    return this.name;
  };
}

function before(target, property, descripot) {
  let oldMthod = descripot.value; // descripot.value获取旧方法
  // 重写方法
  descripot.value = function () {
    console.log("before exect"); // 执行前先做的事
    oldMthod.call(this, ...arguments); // 执行真正的方法
  };
}

@flag
class Animal {
  constructor(name) {
    this.name = name;
  }

  @before
  sayHi() {
    console.log(this.getName() + " say:'hi'");
  }
}

let monkey = new Animal("monkey");

monkey.sayHi(); // 控制台会打印出: before exect