Decorator Function 是 ES7 中一个新增的语言特性,它可以给 JavaScript 的类和方法增加各种功能,包括但不限于:修改类的结构、以属性的形式包装函数、给函数添加元数据等等。Decorator Function 与目前流行的面向切面编程(AOP)思想非常相似,是前端开发中的一个重要工具。
基本语法
Decorator Function 是一个普通函数,它的语法如下所示:
@decorator class MyClass { }
@decorator
是一个装饰器,它以 @
符号开头,后面跟着一个函数(装饰器函数),这个函数可以包装类、类的属性或者类的方法。装饰器函数接收三个参数:被封装对象的原型、成员名(如果是成员的话)或者类本身,还有一个可选参数 descriptor
,这个参数包含了要修改的对象的一些属性。
类装饰器
类装饰器是最常见的一种装饰器,它可以用来修改类的结构。我们可以使用类装饰器来添加新方法、属性,也可以通过检查原型上的属性来修改类的行为。
添加属性
首先,我们来看一下如何使用类装饰器来添加一个静态属性到一个类上。
function customAttribute(target) { target.custom = true; } @customAttribute class MyClass { } console.log(MyClass.custom); // 输出 true
上面的代码中,customAttribute
函数是一个类装饰器函数,它接收的参数 target
是被封装对象的构造函数。我们在函数中给这个构造函数添加了一个 custom
静态属性。使用 @
符号来声明类装饰器,用装饰器函数来装饰被封装对象。
添加方法
类装饰器不仅可以添加属性,也可以添加方法。我们可以在类上面使用一个装饰器来添加一个实例方法。
// javascriptcn.com 代码示例 function runOnce(target, name, descriptor) { const original = descriptor.value; descriptor.value = function(...args) { if (!target.hasRun) { this.hasRun = true; return original.apply(this, args); } } return descriptor; } class MyClass { @runOnce doSomething() { console.log('doSomething has been run'); } } const myClass = new MyClass(); myClass.doSomething(); // Output: doSomething has been run myClass.doSomething(); // Output: undefined
上面这个例子中,我们使用 @runOnce
装饰器来修饰 doSomething
方法。装饰器接受三个参数:被封装类的原型、成员的名称和描述符(包含了方法的可枚举性、可修改性、可配置性等信息)。我们首先保存原始的方法,然后将其重写为一个没有返回值的函数,如果这个类的 hasRun
属性还没被设置,再将它设置为真值(true)。否则,直接返回 undefined
。
修改类的行为
类装饰器不光可以添加新方法、属性,还可以通过检查原型上的属性来修改类的行为。如下所示,我们可以使用一个类装饰器来禁止一个类的所有方法修改对象的状态。
// javascriptcn.com 代码示例 function immutable(target) { const methods = Object.getOwnPropertyNames(target.prototype) .filter(function(prop) { return typeof target.prototype[prop] === 'function'; }); methods.forEach(function(method) { const original = target.prototype[method]; target.prototype[method] = function(...args) { const result = original.apply(this, args); if (typeof result !== 'object') { return result; } return Object.freeze(result); }; }); Object.freeze(target.prototype); } @immutable class ImmutableUser { constructor(name, age) { this.name = name; this.age = age; } get nameAndAge() { return `${this.name} ${this.age}`; } set nameAndAge(value) { [this.name, this.age] = value.split(' '); } } const immutableUser = new ImmutableUser('John', 30); immutableUser.nameAndAge = 'Jane 25'; console.log(immutableUser.nameAndAge); // Output: John 30
上面的这个例子中,我们使用 immutable
类装饰器函数将 ImmutableUser
类变成了一个不可变类。immutable
函数通过遍历 target.prototype
上的方法,对每个方法进行修改,防止它们对类实例的状态进行任何修改。具体地,在每个函数调用结束之后,我们都将函数返回值进行了一次 Object.freeze
操作,来防止函数返回一个可变对象。
方法装饰器
方法装饰器是指用来装饰类的方法的装饰器。它的原理与类装饰器类似,只是装饰的对象不同了。
// javascriptcn.com 代码示例 function logger(target, name, descriptor) { const original = descriptor.value; descriptor.value = function(...args) { console.log(`Calling ${name} with ${args}`); return original.apply(this, args); } return descriptor; } class MyClass { @logger doSomething(arg1, arg2) { console.log('Doing Something...'); return arg1 + arg2; } } const myClass = new MyClass(); myClass.doSomething(1, 2); // Output: Calling doSomething with 1,2
上面的例子中,我们使用 @logger
装饰器来修饰 doSomething
方法。在 logger
函数中,我们保存了原始函数,将其替换为一个新函数,该新函数先记录被调用的方法和参数,再调用原始方法,并返回原始方法的返回值。
属性装饰器
Decorator Function 还可以用来装饰类的属性。可以通过属性装饰器来给 JavaScript 对象或类的属性添加元数据、类型注解或者过滤某些数据等等。
// javascriptcn.com 代码示例 function validate(target, name, descriptor) { const original = descriptor.value; descriptor.value = function(...args) { const [age] = args; if (age < 0 || typeof age !== 'number') { throw new Error('Invalid age'); } else { return original.apply(this, args); } } return descriptor; } class User { constructor(name) { this.name = name; } @validate set age(value) { this._age = value; } get age() { return this._age; } } const user = new User('John'); user.age = 30; console.log(user.age); // Output: 30 user.age = -1; // Throws an error
在这个例子中,我们使用 @validate
装饰器来验证 User
类的 age
属性。validate
函数检查传入的参数,若参数不合规,则返回一个错误。否则,继续调用原始函数。
总结
Decorator Function 是 ES7 中一个非常有用的语言特性。它可以用来给 JavaScript 的类和方法添加所有各种功能。我们可以使用类装饰器来添加新方法、属性,也可以通过检查原型上的属性来修改类的行为。我们也可以使用方法装饰器来装饰类的方法,我们还可以使用属性装饰器来给 JavaScript 对象或类的属性添加元数据、类型注解或者过滤某些数据等等。掌握 Decorator Function 在前端开发中的使用,能大大提升我们的工作效率和代码质量。
来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/653f5ea37d4982a6eb8eaadb