详解ES7 Decorator 入门解析


Posted in Javascript onFebruary 18, 2019

Decorator 提供了一种独特的抽象逻辑,可在原有代码基础上,零侵入添加新功能特性。商业代码总是多种交织并存的,在日常开发中,除了实现业务功能之外,我们还需要考虑诸如:异常处理、性能分析、日志等额外的需求。未经设计的的开发方法会倾向于将各种需求耦合组成一个功能模块,比如:

class Math{
 static add(num1,num2){
  try{
   console.time('some label');
   log('log for something');
   const result= num1+num2;
   console.timeEnd('some label');
   return result;
  }catch(e){
   error('something had broken');
  }
 }
}

上述简单的两数相加功能,在添加各类需求之后,已经变的面目全非。Decorator 语法通过描述,可将功能特性叠加到原有功能中:

class Math{
 @log
 @error
 @time
 static add(num1,num2){
  return num1+num2;
 }
}

Decorator 是什么

Decorator 就是一个的包裹函数,运行时在编译阶段调用该函数,修改目标对象的行为、属性。我们先来看一个简单实例:

const log = (target,prop)=>console.log(`Wrap function: '${prop}'`);

const tec={
 @log
 say(){
  console.log('hello world')
 }
}

// => Wrap function 'say'

Decorator 函数签名如下:

// @param  target 作用对象
// @param  prop  作用的属性名
// @param  descriptor 属性描述符
// @return descriptor 属性描述符
function decorator(target,prop,descriptor){}

参数详解:

  • target : 作用的对象,有如下情况:
    • 作用于 class 时,target 为该 class 函数
    • 作用于 class 中的函数、属性 时,target 为该 class 的 prototype 对象
    • 作用于 对象字面量中的函数、属性 时,target 为该对象
  • prop : 描述的属性名,若decorator作用于class时,该参数为空
  • descriptor : 属性原本的描述符,该描述符可通过Object.getOwnPropertyDescriptor() 获取,若decorator作用于class时,该参数为空
  • decorator 函数支持返回描述符或 undefined,当返回值为描述符时,运行时会调用Object.defineProperty()修改原有属性。

Decorator 的ES5实现

理解 Decorator 机制,最佳方式是使用ES5实现该过程。

class装饰器机制比较简单,仅做一层包装,伪代码:

// 调用实例
@log 
class Person{}
// 实现代码
const Person = log(Person);

属性装饰器机制则比较复杂,babel 就此提供了一个参考范例:

// decorator 处理
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;
}

// 调用实例
class Person{
 @log
 say(){}
}

// 实现代码
_applyDecoratedDescriptor(
 Person.prototype, 
 'say', 
 [log],
 Object.getOwnPropertyDescriptor(Person.prototype, 'say'),
 Person.prototype)
)

用例

Decorator 主要应用于如下几类对象:

  1. class
  2. class 中,除构造函数外的方法
  3. class 中的属性
  4. 对象字面量中的函数
  5. 对象字面量中的属性
// 类
@log
class Person{
 // 函数
 @log
 say(){}
 
 // 属性
 @log
 name = 'tec';
}

// 同样适用于对象字面量的方法、属性
const tec = {
 @log
 name:'tec',
 
 @log
 walk(){}
};

Decorator 实践

在JS中,Decorator 是一个新概念,对于多数没有接触过诸如python、C#的开发者而言,很难理解实际应用场景。幸运的是github已经有人封装了常用Decorator。笔者分析该库,总结如下几种定义模式:

通过 descriptor 的 value 值修改:

function decorate(target, key, descriptor) {
 const fn = descriptor.value;

 return {
  ...descriptor,
  value() {
   return fn.apply(this, arguments);
  }
 }
}

通过 descriptor 的 get、set 函数修改:

function decorate(target, key, descriptor) {
 let value = descriptor.value;

 return {
  ...descriptor,
  get() {
   return value;
  }
  set(v) {
   value=v;
  }
 }
}

通过 descriptor 的 writable、enumerable 等属性修改:

function readonly(target, key, descriptor) {
 return {
  ...descriptor,
  writable:false
 }
}

针对 class ,返回包裹函数

function log(target){
 let initTimes=0;
 return (...arg)=>{
  console.log(++initTimes);
  target.call(this,...arg);
 };
}

在实际开发中,还需要注意以下事项:

  • Decorator 的目标是在原有功能基础上,添加功能,切忌覆盖原有功能
  • Decorator 不是管道模式,decorator之间不存在交互,所以必须注意保持decorator独立性、透明性
  • Decorator 更适用于非业务功能需求
  • 确定 decorator 的用途后,切记执行判断参数类型
  • decorator 针对每个装饰目标,仅执行一次

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
彻底搞懂JS无缝滚动代码
Jan 03 Javascript
javascript数组组合成字符串的脚本
Jan 06 Javascript
js中top/parent/frame概述及案例应用
Feb 06 Javascript
充分发挥Node.js程序性能的一些方法介绍
Jun 23 Javascript
javascript获取本机操作系统类型的方法
Aug 13 Javascript
jQuery配合coin-slider插件制作幻灯片效果的流程解析
May 13 Javascript
jQuery简单实现彩色云标签效果示例
Aug 01 Javascript
jQuery使用$获取对象后检查该对象是否存在的实现方法
Sep 04 Javascript
详解webpack与SPA实践之开发环境搭建
Dec 18 Javascript
详解React项目如何修改打包地址(编译输出文件地址)
Mar 21 Javascript
解决vue项目F5刷新mounted里的函数不执行问题
Nov 05 Javascript
js+canvas实现图片格式webp/png/jpeg在线转换
Aug 22 Javascript
jQuery插件实现非常实用的tab栏切换功能【案例】
Feb 18 #jQuery
详解关于微信setData回调函数中的坑
Feb 18 #Javascript
jQuery实现的五星点评功能【案例】
Feb 18 #jQuery
JS中min函数实例讲解
Feb 18 #Javascript
jQuery实现的隔行变色功能【案例】
Feb 18 #jQuery
小程序实现列表多个批量倒计时
Jan 29 #Javascript
记一次vue-webpack项目优化实践详解
Feb 17 #Javascript
You might like
PHP has encountered an Access Violation 错误的解决方法
2010/01/17 PHP
PHP中如何调用webservice的实例参考
2013/04/25 PHP
PHP获取数组的键与值方法小结
2015/06/13 PHP
Yii2框架自定义验证规则操作示例
2019/02/08 PHP
ThinkPHP框架整合微信支付之Native 扫码支付模式二图文详解
2019/04/09 PHP
JavaScript 基础问答三
2008/12/03 Javascript
js计算页面刷新的次数
2009/07/20 Javascript
模仿JQuery.extend函数扩展自己对象的js代码
2009/12/09 Javascript
javascript操作css属性
2013/12/30 Javascript
禁用JavaScript控制台调试的方法
2014/03/07 Javascript
jQuery中:input选择器用法实例
2015/01/03 Javascript
javascript模拟php函数in_array
2015/04/27 Javascript
json+jQuery实现的无限级树形菜单效果代码
2015/08/27 Javascript
Angular实现form自动布局
2016/01/28 Javascript
js判断所有表单项不为空则提交表单的实现方法
2016/09/09 Javascript
BootStrap selectpicker后台动态绑定数据
2017/06/01 Javascript
JavaScript定义函数_动力节点Java学院整理
2017/06/27 Javascript
WebSocket的简单介绍及应用
2019/05/23 Javascript
JS实现表单中点击小眼睛显示隐藏密码框中的密码
2020/04/13 Javascript
[12:51]71泪洒现场!是DOTA2让经典重现
2014/03/24 DOTA
[01:08]2014DOTA2展望TI 剑指西雅图LGD战队专访
2014/06/30 DOTA
[03:42]2014DOTA2西雅图国际邀请赛7月9日TOPPLAY
2014/07/09 DOTA
[14:50]2018DOTA2亚洲邀请赛开幕式
2018/04/03 DOTA
全面解析Python的While循环语句的使用方法
2015/10/13 Python
搭建Python的Django框架环境并建立和运行第一个App的教程
2016/07/02 Python
Python3.6简单操作Mysql数据库
2017/09/12 Python
Python基于pandas实现json格式转换成dataframe的方法
2018/06/22 Python
OpenCV+Python识别车牌和字符分割的实现
2019/01/31 Python
python简单的三元一次方程求解实例
2020/04/02 Python
Urban Outfitters美国官网:美国生活方式品牌
2016/08/26 全球购物
投标单位介绍信
2014/01/09 职场文书
汽车队司机先进事迹材料
2014/02/01 职场文书
学校师德承诺书
2014/05/23 职场文书
舞蹈教育学专业自荐信
2014/06/15 职场文书
4S店收银员岗位职责
2015/04/07 职场文书
初中化学教学反思
2016/02/22 职场文书