浅谈js中的装饰器
栏目: JavaScript · 发布时间: 7年前
内容简介:装饰器模式JS中的装饰器是ES7中的一个新语法,可以对大家可能听说过 组合函数 和 高阶函数 的概念,也可以这么理解。
装饰器模式 (Decorator Pattern)
是一种结构型设计模式,旨在促进代码复用,可以用于修改现有的系统,希望在系统中为对象添加额外的功能,同时又不需要大量修改原有的代码。
JS中的装饰器是ES7中的一个新语法,可以对 类
、 方法
、 属性
进行修饰,从而进行一些相关功能定制, 它的写法与 Java 的注解 (Annotation)
类似,但是功能有比较大的区别。
大家可能听说过 组合函数 和 高阶函数 的概念,也可以这么理解。
我们先来看一下以下代码:
function doSomething(name) {
console.log('Hi, I\'' + name);
}
funtion useLogging(func, name) {
console.log('Starting');
func(name);
console.log('Finished');
}
复制代码
以上逻辑不难理解,给原有的函数加一个打日志的功能,但是这样的话,每次都要传参数给 useLogging
,而且破坏了之前的代码结构,之前直接 doSomething
就好了,现在要改成 useLogging(doSomething, 'Jiang')
。
那有没有更好的方式呢,当然有啦。
简单装饰器:
function useLogging(func) {
return function() {
console.log('Starting');
const result = func.apply(this, arguments)
console.log('Done');
return result;
}
}
const wrapped = useLogging(doSomething);
复制代码
以上代码返回了一个新的函数 wrapped , 调用方式和 doSomething
相同,在原来的基础上能做多一点事情。
doSomething('angry');
// Hi, I'angry
const wrapped = useLogging(doSomething);
wrapped('angry');
// Starting
// Hi, I'angry
// Done
复制代码
怎么使用装饰器?
装饰器主要有两种用法:
- 装饰类方法或属性(类成员)
class MyClass {
@readonly
method() { }
}
function readonly(target, name, descriptor) {
descriptor.writable = false;
return descriptor;
}
复制代码
- 装饰类
@annotation
class MyClass { }
function annotation(target) {
target.annotated = true;
}
复制代码
类成员装饰器
类成员装饰器用来装饰类里面的属性、方法、 getter
和 setter
。这个装饰器函数调用三个参数调:
-
target: 被装饰的类的原型 -
name: 被装饰的类、属性、方法的名字 -
descriptor: 被装饰的类、属性、方法的descriptor,将传递给Object.defineProperty
我们来写几个装饰器,代码如下:
写一个 @readonly
装饰器,简单版实现:
class Example {
@log
add(a, b) {
return a + b;
}
@unenumerable
@readonly
name = "alibaba"
}
function readonly(target, name, descriptor) {
descriptor.writable = false;
return descriptor;
}
function unenumerable(target, name, descriptor) {
descriptor.enumerable = false;
return descriptor;
}
function log(target, name, descriptor) {
const original = descriptor.value;
if (typeof original === 'function') {
descriptor.value = function(...args) {
console.log(`Arguments: ${args}`);
try {
const result = original.apply(this, args);
console.log(`Result: ${result}`);
return result;
} catch (e) {
console.log(`Error: ${e}`);
throw e;
}
}
}
return descriptor;
}
const e = new Example();
// Calling add with [2, 4]
e.add(2, 4);
e.name = 'antd'; // Error
复制代码
我们可以通过 Babel
查看编译后的代码,也可以在本地编译。
npm i @babel/core @babel/cli npm i @babel/plugin-proposal-decorators @babel/plugin-proposal-class-properties -D 复制代码
.babelrc
文件
{
"plugins": [
["@babel/plugin-proposal-decorators", { "legacy": true }],
["@babel/plugin-proposal-class-properties", {"loose": true}]
]
}
复制代码
编译 ES6 语法输出到文件
因为没用全局安装 @babel/cli
, 建议用 npx 命令来执行,或者 ./node_modules/.bin/babel
,关于 npx
命令,可以看下官方文档
npx babel decorator.js --out-file complied.js 复制代码
编译后的代码:
function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) {
var desc = {};
// 拷贝属性
Object['ke' + 'ys'](descriptor).forEach(function (key) {
desc[key] = descriptor[key];
});
desc.enumerable = !!desc.enumerable;
desc.configurable = !!desc.configurable;
if ('value' in desc || desc.initializer) {
desc.writable = true;
}
desc = decorators.slice().reverse().reduce(function (desc, decorator) {
return decorator(target, property, desc) || desc;
}, desc);
if (context && desc.initializer !== void 0) {
desc.value = desc.initializer ? desc.initializer.call(context) : void 0; desc.initializer = undefined;
}
if (desc.initializer === void 0) {
Object['define' + 'Property'](target, property, desc); desc = null;
}
return desc;
}
_applyDecoratedDescriptor(_class.prototype, "add", [log], Object.getOwnPropertyDescriptor(_class.prototype, "add"), _class.prototype)
复制代码
Babel 构建了一个 _applyDecoratedDescriptor
函数,用于装饰类成员
Object.getOwnPropertyDescriptor
Object.getOwnPropertyDescriptor()
方法返回指定对象上一个自有属性对应的属性描述符。(自有属性指的是直接赋予该对象的属性,不需要从原型链上进行查找的属性),不是原型链上的这点很关键。
详情可以查看官方文档,这里就不细说了。
var desc = {};
// 这里对 descriptor 属性做了一层拷贝
Object['ke' + 'ys'](descriptor).forEach(function (key) {
desc[key] = descriptor[key];
});
desc.enumerable = !!desc.enumerable;
desc.configurable = !!desc.configurable;
// 没有 value 或者 initializer 属性的,都是 get 和 set 方法
if ('value' in desc || desc.initializer) {
desc.writable = true;
}
复制代码
这里的 initializer 是 Babel 为了配合 decorator 而产生的一个属性,就比方说对于上面代码中的 name 属性,被编译成:
_descriptor = _applyDecoratedDescriptor(_class.prototype, "name", [unenumerable, readonly], {
configurable: true,
enumerable: true,
writable: true,
initializer: function initializer() {
return "alibaba";
}
})
复制代码
desc = decorators.slice().reverse().reduce(function (desc, decorator) {
return decorator(target, property, desc) || desc;
}, desc);
复制代码
处理多个 decorator 的情况,这里执行了slice()和reverse(),所以我们可以得出,一个类成员有多个装饰器,会由内向外执行。
if (context && desc.initializer !== void 0) {
desc.value = desc.initializer ? desc.initializer.call(context) : void 0;
desc.initializer = undefined;
}
if (desc.initializer === void 0) {
Object['define' + 'Property'](target, property, desc); desc = null;
}
return desc;
复制代码
最后无论是装饰方法还是属性,都会执行:
Object["define" + "Property"](target, property, desc); 复制代码
由此可见,装饰方法本质上还是使用 Object.defineProperty() 来实现的。
类装饰器
类装饰器相对简单
function log(Class) {
return (...args) => {
console.log(args);
return new Class(...args);
};
}
复制代码
@log
class Example {
constructor(name, age) {
}
}
const e = new Example('Graham', 34);
// [ 'Graham', 34 ]
console.log(e);
// Example {}
复制代码
装饰器中传入参数:
function log(name) {
return function decorator(Class) {
return (...args) => {
console.log(`Arguments for ${name}: args`);
return new Class(...args);
};
}
}
@log('Demo')
class Example {
constructor(name, age) {}
}
const e = new Example('Graham', 34);
// Arguments for Demo: args
console.log(e);
// Example {}
复制代码
应用
在 React 中,经常会用到 redux 或者高阶组件。
class A extends React.Component {}
export default connect()(A);
复制代码
装饰器写法:
@connect() export default connect()(A); 复制代码
以上所述就是小编给大家介绍的《浅谈js中的装饰器》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
The Mechanics of Web Handling
David R. Roisum
This unique book covers many aspects of web handling for manufacturing, converting, and printing. The book is applicable to any web including paper, film, foil, nonwovens, and textiles. The Mech......一起来看看 《The Mechanics of Web Handling》 这本书的介绍吧!