详解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 相关文章推荐
jQuery getJSON()+.ashx 实现分页(改进版)
Mar 28 Javascript
使用CoffeeScrip优美方式编写javascript代码
Oct 28 Javascript
JavaScript位移运算符(无符号) >>> 三个大于号 的使用方法详解
Mar 31 Javascript
JS仿百度自动下拉框模糊匹配提示
Jul 25 Javascript
JS使用正则表达式实现关键字替换加粗功能示例
Aug 03 Javascript
js实现PC端和移动端刮卡效果
Mar 27 Javascript
javascript中toFixed()四舍五入使用方法详解
Sep 28 Javascript
vue中tab选项卡的实现思路
Nov 25 Javascript
使用jquery模拟a标签的click事件无法实现跳转的解决
Dec 04 jQuery
javascript实现对话框功能警告(alert 消息对话框)确认(confirm 消息对话框)
May 07 Javascript
浅谈vue 二级路由嵌套和二级路由高亮问题
Aug 06 Javascript
js实现滑动进度条效果
Aug 21 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
我的论坛源代码(四)
2006/10/09 PHP
使用 PHPMAILER 发送邮件实例应用
2012/11/07 PHP
PHP CURL CURLOPT参数说明(curl_setopt)
2013/09/30 PHP
PHP使用DOMDocument类生成HTML实例(包含常见标签元素)
2014/06/25 PHP
PHP判断数据库中的记录是否存在的方法
2014/11/14 PHP
ThinkPHP表单数据智能写入create方法实例分析
2015/09/27 PHP
PHP时间戳和日期相互转换操作实例小结
2018/12/18 PHP
Thinkphp框架使用list_to_tree 实现无限级分类列出所有节点示例
2020/04/04 PHP
[原创]提供复制本站内容时出现,该文章转自脚本之家等字样的js代码
2007/03/27 Javascript
JS backgroundImage控制
2009/05/19 Javascript
js function定义函数使用心得
2010/04/15 Javascript
JavaScript 类的定义和引用 JavaScript高级培训 自定义对象
2010/04/27 Javascript
javascript判断用户浏览器插件安装情况的代码
2011/01/01 Javascript
jQuery的attr与prop使用介绍
2013/10/10 Javascript
jquery.autocomplete修改实现键盘上下键自动填充示例
2013/11/19 Javascript
JavaScript中标识符提升问题
2015/06/11 Javascript
常见JS验证脚本汇总
2015/12/01 Javascript
jQuery遮罩层实现方法实例详解(附遮罩层插件)
2015/12/08 Javascript
Javascript基于对象三大特性(封装性、继承性、多态性)
2016/01/04 Javascript
Vue.js开发环境搭建
2016/11/10 Javascript
ES6中Proxy与Reflect实现重载(overload)的方法
2017/03/30 Javascript
jquery插件canvaspercent.js实现百分比圆饼效果
2017/07/18 jQuery
解决axios发送post请求返回400状态码的问题
2018/08/11 Javascript
JS中验证整数和小数的正则表达式
2018/10/08 Javascript
[49:43]VG vs FNATIC 2019国际邀请赛小组赛 BO2 第一场 8.15
2019/08/17 DOTA
Win10下python3.5和python2.7环境变量配置教程
2018/09/18 Python
实例详解Python装饰器与闭包
2019/07/29 Python
python安装dlib库报错问题及解决方法
2020/03/16 Python
Python任务调度利器之APScheduler详解
2020/04/02 Python
canvas绘图按照contain或者cover方式适配并居中显示
2019/02/18 HTML / CSS
部队党性分析材料
2014/02/16 职场文书
项目采购员岗位职责
2014/04/15 职场文书
校园文化标语
2014/06/18 职场文书
放牛班的春天观后感
2015/06/01 职场文书
《正比例》教学反思
2016/02/23 职场文书
如何计划开一家便利店?
2019/07/31 职场文书