Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

面试官:JavaScript修饰器:简化代码,增强功能 #26

Open
linwu-hi opened this issue Jul 30, 2023 · 0 comments
Open

面试官:JavaScript修饰器:简化代码,增强功能 #26

linwu-hi opened this issue Jul 30, 2023 · 0 comments

Comments

@linwu-hi
Copy link
Owner

JavaScript修饰器:简化代码,增强功能

引言

在JavaScript中,修饰器(Decorator)是一种特殊的语法,用于修改类、方法或属性的行为。修饰器提供了一种简洁而灵活的方式来扩展和定制代码功能。本文将详细介绍JavaScript修饰器的概念、语法和应用场景,并提供相关的代码示例。

1. 修饰器简介

修饰器是一种用于修改类、方法或属性的语法,它可以在不修改原始代码的情况下增强其功能。修饰器可以实现横切关注点(cross-cutting concerns)的功能,例如日志记录、性能分析、缓存等。通过将这些功能与原始代码分离,我们可以更好地组织和维护代码,并实现更高的可重用性和可扩展性。

2. 修饰器语法

修饰器使用@符号作为前缀,紧跟着修饰器函数或类。修饰器可以接收不同的参数,根据修饰的目标不同,参数也会有所区别。修饰器可以单独使用,也可以通过组合多个修饰器来实现更复杂的功能。

下面是一个基本的修饰器语法示例:

@decorator
class MyClass {
  @propertyDecorator
  myProperty = 123;

  @methodDecorator
  myMethod() {
    // 代码逻辑
  }
}

3. 类修饰器

应用场景

类修饰器用于修改类的行为和属性。它可以在类定义之前应用,以修改类的构造函数或原型。

常见的应用场景包括:

  • 日志记录:在类的方法执行前后记录日志信息。
  • 验证和授权:对类的方法进行验证和授权操作。
  • 性能分析:测量类的方法执行时间,进行性能分析。
  • 依赖注入:为类的构造函数注入依赖项。

示例代码

下面是一个使用类修饰器实现日志记录

的示例:

function log(target) {
  const originalConstructor = target;

  function newConstructor(...args) {
    console.log(`Creating instance of ${originalConstructor.name}`);
    return new originalConstructor(...args);
  }

  return newConstructor;
}

@log
class MyClass {
  constructor(name) {
    this.name = name;
  }
}

const myObj = new MyClass("John");

在上面的示例中,我们定义了一个名为log的修饰器函数。该修饰器函数接收一个参数target,表示要修饰的类构造函数。在修饰器函数内部,我们将原始的构造函数保存到originalConstructor中,并创建一个新的构造函数newConstructor,该构造函数在创建实例前打印日志信息。最后,我们将新的构造函数返回作为修饰后的类构造函数。

4. 方法修饰器

应用场景

方法修饰器用于修改类的方法行为。它可以在方法定义之前应用,以修改方法的特性和行为。

常见的应用场景包括:

  • 日志记录:在方法执行前后记录日志信息。
  • 验证和授权:对方法进行验证和授权操作。
  • 性能分析:测量方法执行时间,进行性能分析。
  • 缓存:为方法添加缓存功能,提高性能。

示例代码

下面是一个使用方法修饰器实现日志记录的示例:

function log(target, name, descriptor) {
  const originalMethod = descriptor.value;

  descriptor.value = function(...args) {
    console.log(`Executing method ${name}`);
    const result = originalMethod.apply(this, args);
    console.log(`Method ${name} executed`);
    return result;
  };

  return descriptor;
}

class MyClass {
  @log
  myMethod() {
    // 代码逻辑
  }
}

const myObj = new MyClass();
myObj.myMethod();

在上面的示例中,我们定义了一个名为log的修饰器函数。该修饰器函数接收三个参数,分别是target(类的原型或构造函数)、name(方法名)和descriptor(方法的属性描述符)。在修饰器函数内部,我们获取原始方法并将其保存到originalMethod中。然后,我们修改descriptor.value,将其替换为一个新的函数,该函数在执行原始方法前后打印日志信息。最后,我们返回修改后的属性描述符。

5. 属性修饰器

应用场景

属性修饰器用于修改类的属性行为。它可以在属性定义之前应用,以修改属性的特性和行为。

常见的应用场景包括:

  • 日志记录:在属性读取或写入时记录日志信息。
  • 验证和授权:对属性进行验证和授权操作。
  • 计算属性:根据其他属性的值计算属性的值。
  • 缓存:为属性添加

缓存功能,提高性能。

示例代码

下面是一个使用属性修饰器实现日志记录的示例:

function log(target, name) {
  let value;

  const getter = function() {
    console.log(`Getting value of property ${name}`);
    return value;
  };

  const setter = function(newValue) {
    console.log(`Setting value of property ${name}`);
    value = newValue;
  };

  Object.defineProperty(target, name, {
    get: getter,
    set: setter,
    enumerable: true,
    configurable: true
  });
}

class MyClass {
  @log
  myProperty;
}

const myObj = new MyClass();
myObj.myProperty = 123;
const value = myObj.myProperty;

在上面的示例中,我们定义了一个名为log的修饰器函数。该修饰器函数接收两个参数,分别是target(类的原型或构造函数)和name(属性名)。在修饰器函数内部,我们定义了一个名为getter的函数,用于获取属性值,并在获取属性值时打印日志信息。我们还定义了一个名为setter的函数,用于设置属性值,并在设置属性值时打印日志信息。最后,我们使用Object.defineProperty方法将修饰后的属性定义到类的原型上。

6. 参数修饰器

应用场景

参数修饰器用于修改方法的参数行为。它可以在方法参数声明之前应用,以修改参数的特性和行为。

常见的应用场景包括:

  • 验证和授权:对方法的参数进行验证和授权操作。
  • 日志记录:在方法执行前后记录参数信息。
  • 参数转换:对方法的参数进行类型转换或格式化操作。

示例代码

下面是一个使用参数修饰器实现参数验证的示例:

function validate(target, name, index, validator) {
  const originalMethod = target[name];

  target[name] = function(...args) {
    const value = args[index];
    if (validator(value)) {
      return originalMethod.apply(this, args);
    } else {
      throw new Error(`Invalid value for parameter ${index} of method ${name}`);
    }
  };
}

class MyClass {
  myMethod(@validate isNumber) {
    // 代码逻辑
  }
}

function isNumber(value) {
  return typeof value === "number";
}

const myObj = new MyClass();
myObj.myMethod(123);

在上面的示例中,我们定义了一个名为validate的修饰器函数。该修饰器函数接收四个参数,分别是target(类的原型或构造函数)、name(方法名)、index(参数索引)和validator(验证函数)。在修饰器函数内部,我们获取原始方法并将其保存到originalMethod中。然后,我们修改target[name],将其替换为一个新的函数,该函数在执行原始方法之前对指定参数进行验证。如果参数通过验证,就继续执行原始方法;否则,抛出一个错误

。最后,我们使用@validate修饰器应用参数验证。

7. 修饰器组合和执行顺序

可以通过组合多个修饰器来实现更复杂的功能。修饰器的执行顺序从上到下,从右到左。

以下是一个使用多个修饰器组合的示例:

function log(target, name, descriptor) {
  // 日志记录逻辑
}

function validate(target, name, index, validator) {
  // 参数验证逻辑
}

class MyClass {
  @log
  @validate(isNumber)
  myMethod(@validate(isString) param1, @validate(isBoolean) param2) {
    // 代码逻辑
  }
}

在上面的示例中,我们通过使用@log修饰器和@validate修饰器组合,为类的方法和参数添加日志记录和验证功能。修饰器的执行顺序是从上到下,从右到左。

8. 常用修饰器库和工具

除了原生的修饰器语法,还有许多优秀的修饰器库和工具可供使用。一些常见的库和工具包括:

  • core-decorators:提供了一组常用的修饰器,如@readonly@debounce@throttle等。GitHub 地址
  • lodash-decorators:基于Lodash库的修饰器集合,提供了许多实用的修饰器。GitHub 地址
  • mobx:流行的状态管理库MobX使用修饰器来实现响应式数据和自动触发更新。官方文档
  • nestjs:基于Node.js的框架NestJS使用修饰器来实现依赖注入、路由定义等功能。官方文档

9. 结论

JavaScript修饰器是一种强大的语法,它能够简化代码、增强功能,并提高代码的可维护性和可扩展性。通过使用修饰器,我们可以轻松地实现日志记录、验证和授权、性能分析等常见的功能,同时保持代码的整洁和可读性。修饰器在许多库和框架中得到了广泛的应用,为开发者提供了更好的开发体验和工具支持。

10. 参考资料

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant