JavaScript 装饰器:一个高端玩具还是一个必备工具?
在 JavaScript 中,装饰器是一种能够装饰函数和类的语言特性。通过装饰器,我们可以为函数和类添加各种有用的功能,比如日志记录、输入验证、结果缓存、错误处理等,而无需修改原始函数或类的代码。本文将介绍如何使用装饰器,并提供一些示例和使用场景。
如何使用装饰器
在 JavaScript 中,装饰器是一种基于元编程的语言特性,可以在运行时修改函数或类的行为。装饰器是通过在函数或类声明前添加 @ 符号来使用的,比如:
@decorator function foo() {}
或者
@decorator
class Bar {}
装饰器函数是一个普通函数,它接收三个参数:
target
:被装饰的函数或类。name
:被装饰的方法名或类名。descriptor
:被装饰方法的描述符。
装饰器函数可以修改 descriptor.value
来修改方法的实现,或者返回一个新的描述符来改变方法的行为。
下面是一个简单的示例,使用装饰器为 sum
函数添加日志记录的功能:
function log(target, name, descriptor) {
const original = descriptor.value;
descriptor.value = function (...args) {
console.log(`[${name}] called with args:`, args);
const result = original.apply(this, args);
console.log(`[${name}] returned:`, result);
return result;
};
return descriptor;
}
class Calculator {
@log
static sum(a, b) {
return a + b;
}
}
console.log(Calculator.sum(1, 2)); // [sum] called with args: [1, 2]
// [sum] returned: 3
// 3
在上面的示例中,我们定义了一个 log
装饰器函数,它接收三个参数 target
、name
和 descriptor
。在 descriptor.value
中,我们将原始的 sum
函数替换为一个新的函数,新函数在执行 sum
前后都会打印日志。在 Calculator
类中,我们使用 @log
装饰器将 sum
函数装饰起来,使其具有日志记录的功能。当我们调用 Calculator.sum(1, 2)
时,将会打印出日志记录。
装饰器的使用场景
装饰器可以为函数和类添加各种有用的功能,下面是一些常见的使用场景。
1. 日志记录
日志记录是一个常见的需求,可以使用装饰器轻松地为函数和类添加日志记录功能,以便调试和错误处理。
function log(target, name, descriptor) {
const original = descriptor.value;
descriptor.value = function (...args) {
console.log(`[${name}] called with args:`, args);
const result = original.apply(this, args);
console.log(`[${name}] returned:`, result);
return result;
};
return descriptor;
}
class Calculator {
@log
static sum(a, b) {
return a + b;
}
}
console.log(Calculator.sum(1, 2)); // [sum] called with args: [1, 2]
// [sum] returned: 3
// 3
在上面的示例中,我们定义了一个 log
装饰器函数,它接收三个参数 target
、name
和 descriptor
。在 descriptor.value
中,我们将原始的 sum
函数替换为一个新的函数,新函数在执行 sum
前后都会打印日志。在 Calculator
类中,我们使用 @log
装饰器将 sum
函数装饰起来,使其具有日志记录的功能。当我们调用 Calculator.sum(1, 2)
时,将会打印出日志记录。
2. 输入验证
输入验证是一个重要的安全需求,可以使用装饰器轻松地为函数和类添加输入验证功能,以便确保输入参数的有效性。
function validateInputs(target, name, descriptor) {
const original = descriptor.value;
descriptor.value = function (...args) {
if (args.some((arg) => typeof arg !== "number")) {
throw new Error(`[${name}] only accepts numbers as inputs`);
}
return original.apply(this, args);
};
return descriptor;
}
class Calculator {
@validateInputs
static sum(a, b) {
return a + b;
}
}
console.log(Calculator.sum(1, 2)); // 3
console.log(Calculator.sum(1, "2")); // Error: [sum] only accepts numbers as inputs
在上面的示例中,我们定义了一个 validateInputs
装饰器函数,它接收三个参数 target
、name
和 descriptor
。在 descriptor.value
中,我们将原始的 sum
函数替换为一个新的函数,新函数在执行 sum
前会验证输入参数是否为数字类型。如果输入参数不是数字类型,将会抛出一个错误。在 Calculator
类中,我们使用 @validateInputs
装饰器将 sum
函数装饰起来,使其具有输入验证的功能。当我们调用 Calculator.sum(1, "2")
总结
装饰器是一种非常有用的语言特性,可以为函数和类添加各种有用的功能,比如日志记录、输入验证、性能优化等。装饰器是通过在函数和类定义前使用 @
符号来应用的。装饰器本质上是一个高阶函数,它接收目标对象作为参数,并返回一个新的对象,该对象包含被装饰的函数或类。
装饰器可以帮助我们更好地组织代码,并且使代码更加可读、可维护和可扩展。
用装饰器函数实现一个小功能
最后玩一个练习吧,实现一个回文字符串的功能吧,看看我们是否真的学会了
创建一个装饰器函数,将其应用到一个类的方法上。该方法接收一个字符串参数,需要检查该参数是否是回文字符串。如果是回文字符串,则返回原始字符串,否则返回该字符串的反转字符串。