初探ES7 Decorator
装饰器
装饰器是 ES7 新有的特性,它允许我们使用简洁的方式,为已有的类、类的方法、类的属性 添加有趣的修饰。 可使用如下:
// deco.js
// 假设已实现 装饰器 studentDecorator、readonly、shouldITellYou
@studentDecorator
class Person{
@readonly
name = 'carvenzhang';
@shouldITellYou
getInfo(){
//...
}
}
目前,node 还不支持 decorator,但是,感谢 babel,它提供了转译支持。 我们可以运行使用如下:
# 先安装 babel-cli 及 babel-plugin-transform-decorators-legacy
babel --plugins transform-decorators-legacy deco.js > deco.es5.js && node deco.es5.js
实现 Decorator
那么,装饰器要怎么实现了,什么场景下需要用到decorator
呢。
装饰器比较使用于,在完整的系统功能上,提供辅助能力。比如 记录日志 、 查询权限。
目前由一个很好的集成装饰器,可以提供学习:core-decorators。
我们可以通过编写一个权限审核的Decorator,达到学习decorator的目的。
编写一个原始类
我们编写一个很简单的class,模拟一些操作。
// deco.js
class DBAct{
constructor(options){
this._options = Object.assign({
auths:[]
}, options);
}
add(){
console.log('db add');
}
delete(){
console.log('db delete');
}
update(){
console.log('db update');
}
select(){
console.log('db select');
}
}
这是一个数据库操作集合,可以通过调用方法, 实现增删改查。
我们通过编写decorator,为其提供操作数据库前的权限审核能力。
实现方法型decorator
首先,我们实现一个 decoraotr,为每个方法提供一个权限检查的能力。
供给方法使用的 decorator 有三个参数,target
、name
、descriptor
,与Object.defineProperty()
的参数一一对应。
target: 类的原型对象,比如 DBAct.prototype name: 要修饰的方法的属性名,比如 add descriptor: 该属性的描述对象,包含 {configurable, enumerable, value, writable } 等值,其中value即实际修饰的函数。
实现如下:
function authMethodDecorator(auth){
//函数封装层,返回decorator
return function(target, name, descriptor){
// 获取原函数
const method = descriptor.value;
// 重写函数
descriptor.value = function(...args) {
// 判断是否有该权限
if(this._options.auths.includes(auth)){
// 继续调用原函数
return method.apply(this, args);
} else {
// 无权限
console.log(`no permission to exec ${name}`)
}
}
return descriptor;
}
}
整合起来调用如下:
// deco.js
class DBAct{
constructor(options){
this._options = Object.assign({
auth:{}
}, options);
}
// 只有在有 add 权限的情况下,才能进行 add 操作。
@authMethodDecorator('add')
add(){
console.log('db add');
}
// 只有在有 delete 权限的情况下,才能进行 delete 操作。
@authMethodDecorator('delete')
delete(){
console.log('db delete');
}
update(){
console.log('db update');
}
select(){
console.log('db select');
}
}
// 实例化
const user1 = new DBAct({
auths:['add'] // user1 仅有 add 权限
});
r1.add();
r1.delete();
// 关于方法的decorator
function authMethodDecorator(auth){
//..
}
最终通过babel转译,node调用,得到以下结果
# exec
babel --plugins transform-decorators-legacy deco.js > deco.es5.js && node deco.es5.js
# echo
db add
no permission to exec delete
实现类的Decorator
我们也可以实现一个Decorator,为整个class的每一个方法都统一做权限检查。 类的decorator仅有一个参数:
target: 调用的类本身,比如 DBAct
实现如下:
function authClassDecorator(auth){
//函数封装层,返回decorator
return function(target){
// 获取 class 上所有的描述对象
const descs = Object.getOwnPropertyDescriptors(target.prototype);
// 获取 class 上所有的属性名,此方法不兼容 Symbol,实际解决方案参考 core-decorator
const keys = Object.keys(descs);
//循环处理每一个方法
for (let i = 0, l = keys.length; i < l; i++) {
const name = keys[i];
const desc = descs[name];
// 不处理 非函数 和 构造函数
if (typeof desc.value !== 'function' || name === 'constructor') {
continue;
}
// 为每个方法分别调用一次 authMethodDecorator,重写给 target.prototype
Object.defineProperty(target.prototype, name, authMethodDecorator(auth)(target.prototype, name, desc));
}
}
}
整合起来调用如下:
// deco.js
//所有方法都需要connect权限才能执行
@authClassDecorator('connect')
class DBAct{
constructor(options){
this._options = Object.assign({
auth:{}
}, options);
}
add(){
console.log('db add');
}
delete(){
console.log('db delete');
}
update(){
console.log('db update');
}
select(){
console.log('db select');
}
}
// 实例化
const user1 = new DBAct({
auths:['add'] // user1 仅有 add 权限
});
r1.add();
r1.delete();
// 关于方法的decorator
function authMethodDecorator(auth){
//..
}
// 关于类的decorator
function authClassDecorator(auth){
//..
}
最终通过babel转译,node调用,得到以下结果
# exec
babel --plugins transform-decorators-legacy deco.js > deco.es5.js && node deco.es5.js
# echo
no permission to exec add
no permission to exec delete
decorator整合
那么,我们能不能实现一个decoraotor,既能给calss用,也能给method用呢? 可以的:
@authDecorator('connect')
class DBAct{
constructor(options){
this._options = Object.assign({
auths:[]
}, options);
}
@authDecorator('add')
add(){
console.log('db add');
}
@authDecorator('delete')
delete(){
console.log('db delete');
}
update(){
console.log('db update');
}
select(){
console.log('db select');
}
}
const user1 = new DBAct({
auths:['add', 'connect'] // user1 仅有 add 权限
});
user1.add();
user1.delete();
function authMethodDecorator(auth, target, name, descriptor){
// 获取原函数
const method = descriptor.value;
// 重写函数
descriptor.value = function(...args) {
// 判断是否有该权限
// if(this === target){
// return method.apply(target, args)
// }
if(this._options.auths.includes(auth)){
// 继续调用原函数
return method.apply(this, args);
} else {
// 无权限
console.log(`no permission to exec ${name}`)
}
}
return descriptor;
}
function authClassDecorator(auth, target){
// 获取 class 上所有的描述对象
const descs = Object.getOwnPropertyDescriptors(target.prototype);
// 获取 class 上所有的属性名,此方法不兼容 Symbol,实际解决方案参考 core-decorator
const keys = Object.keys(descs);
//循环处理每一个方法
for (let i = 0, l = keys.length; i < l; i++) {
const name = keys[i];
const desc = descs[name];
// 不处理 非函数 和 构造函数
if (typeof desc.value !== 'function' || name === 'constructor') {
continue;
}
// 为每个方法分别调用一次 authMethodDecorator,重写给 target.prototype
Object.defineProperty(target.prototype, name, authMethodDecorator(auth, target.prototype, name, desc));
}
}
// 通过参数个数判断是 调用方式 method 还是 class
function authDecorator(auth){
return function handle(...args) {
if (args.length === 1) {
return authClassDecorator(auth, ...args);
}
return authMethodDecorator(auth, ...args);
};
}
最终通过babel转译,node调用,得到以下结果
# exec
babel --plugins transform-decorators-legacy deco.js > deco.es5.js && node deco.es5.js
# echo
db add
no permission to exec delete
这样,一个通用的decorator就出来了。
迁移
我的博客即将搬运同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=jwpzso9zymr
- 3399: [Usaco2009 Mar]Sand Castle城堡
- 遗传算法(1)
- LOJ#6284. 数列分块入门 8
- 3713: [PA2014]Iloczyn
- 洛谷P3195 [HNOI2008]玩具装箱TOY(单调队列优化DP)
- SQL Server 深入解析索引存储(下)
- 2751: [HAOI2012]容易题(easy)
- codevs3002 石子归并 3
- 算法模板——计算几何2(二维凸包——Andrew算法)
- 算法模板——splay区间反转 2
- 算法模板——Dinic网络最大流 2
- 1935: [Shoi2007]Tree 园丁的烦恼
- 1339 / 1163: [Baltic2008]Mafia
- 4010: [HNOI2015]菜肴制作
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法