JavaScript 的装饰器

装饰器的简单说明

装饰器的作用主要是将函数包装到另外一个函数中,这样就可以使用装饰器来扩展某个类而不影响某个类的代码。

使用闭包来实现装饰器

在了解装饰器的作用后,我们可以编写一个用于计算函数调用次数的闭包装饰器来体验一下装饰器的作用。具体的代码如下:

// 计算参数的和
let sum = (...args) => {
  return [...args].reduce((acc, num) => acc + num);
};

// 测试函数
let fn1 = () => {
  console.log(123);
};

// 计算函数被调用的次数
const callCounter = (fn) => {
  let count = 0;

  return (...args) => {
    console.log(`函数执行了${(count += 1)}`);
    return fn(...args);
  };
};

sum = callCounter(sum); // 使用callCounter函数来装饰sum函数
fn1 = callCounter(fn1); // 使用callCounter函数来装饰fn1函数

console.log(sum(1, 2, 3));
console.log(sum(33, 11));
console.log(sum(33));

fn1();
fn1();
fn1();
fn1();

在上述的例子中,我们可以看到callCounter是一个闭包函数,它主要是声明了一个计数器并且接受参数为函数,这样我们就可以在闭包内部来执行接收的函数,并且可以记住函数的调用次数。

闭包实现装饰器第二个例子

// 计算矩形面积
let getArea = (width, height) => {
  return width * height;
};

// 检测函数的个数
const countParams = (fn) => {
  const name = fn.name;

  return (...params) => {
    if (params.length !== fn.length) {
      throw new Error(`函数 ${name} 的参数个数不正确`);
    }

    return fn(...params);
  };
};

// 检测函数的参数是否为正数
const requireIntegers = (fn) => {
  const name = fn.name;

  return (...params) => {
    params.forEach((param) => {
      if (!Number.isInteger(param)) {
        throw new TypeError(`函数 ${name} 的参数必须是整数`);
      }
    });

    return fn(...params);
  };
};

getArea = countParams(getArea);
getArea = requireIntegers(getArea);

console.log(getArea(10, 11));
console.log(getArea(1, 2, 3));
console.log(getArea(1, "123"));

借助 Babel 实现装饰器

上述的两个例子是函数式的装饰器,而我们还可以借助 Babel 来实现另外一种装饰器。接下来我们就来尝试一下另外一种装饰器的写法。

首先我们先安装好如下几个 babel 插件:

  • @babel/cli
  • @babel/core
  • @babel/plugin-proposal-decorators

接下来配置 babel:

{
  "presets": ["@babel/preset-env"],
  "plugins": [["@babel/plugin-proposal-decorators", { "version": "2022-03" }]]
}

属性上添加装饰器

import { log } from "./log.decorator";

class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  @log
  getAge() {
    return `${this.name} is ${this.age} years old.`;
  }
}

let gus = new Person("Tom", 21);
const gusAge = gus.getAge();
console.log(gusAge);
export const log = (value, { kind, name }) => {
  if (kind === "method") {
    return function (...args) {
      console.log(
        `${name} deforator logged at: ${new Date().toLocaleDateString()}`
      );
      try {
        const result = value.apply(this, args);
        return result;
      } catch (e) {
        console.log(`Error: ${e}`);
        throw e;
      }
    };
  }
};

类上添加装饰器

@logged
class Person {}

export const logged = (val, { kind, name }) => {
  if (kind === "class") {
    return class extends val {
      constructor(...args) {
        super(...args);
        console.log(
          `Construction an instance of ${name} with arguments ${args.join(
            ", "
          )}`
        );
      }
    };
  }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值