Decorator 简介及实战

JavaScript   2017-11-24 16:50:14 发布
您的评价:
     
0.0
收藏     0收藏
文件夹
标签
(多个标签用逗号分隔)

前言

用过Java的朋友都知道,装饰器(Decorator)是一种十分非常方便改变类运行是行为的一种方式,例如在 Spring 框架中我们用到比较多的注解(@Autowired), 通过它可以自动实例化对象,从而可以精简代码。装饰器是典型的 AOP(Aspect-Oriented-Programming) 编程的应用,类似的 CSS 对于 HTML 的样式也是类似于这种思想,即从切面来改变和影响主对象的行为。

Decorator 简介

Decorator 是一种通过注解表达式就可以扩展类或者方法的函数。Decorator 可以应用到任何一个 class 或者property 上。列如:

@myDecorator class A {} // 作用class

@myDecorator
doSomething() {}  // 作用表达式

Javascript Decorator 目前任然是ES7提案状态,更多该特性的进度可以访问 proposal-decorators 查看。

Decorator原理

说到更改对象的属性或者方法,大家肯定会想到 Object.defineProperty(obj, prop, descriptor) 方法,通过该方法,我们可以轻易的修改或者重写对象的行为或者属性,之前 Vue 中提到的双向绑定,即是通过重写 set 和 get 方法来实现的。所以在我们还未正式用上 Decorator 前,都是通过 Object.defineProperty 方法来实现。先来简单的认识下这个方法:

/**

  • obj : 需要修改属性的对象
  • prop : 需要修改对象的属性名称
  • descriptor: 用来定义属性具体行为的描述对象
    **/
    Object.defineProperty(obj, prop, descriptor)

descriptor 属性说明

  • configurable : 定义属性对象是否可以被配置,即如果为 false ,定义修改的描述操作(writeable, get 等等)都无效

  • enumerable : 是否可以通过 for-in 来遍历,或者 Object.keys 列举

  • value : 定义对象 value 属性的值,value 可以是 number, object, function 等等

  • writable: 定义 value 值是否可以被重写

  • get: 一个访问 value 属性时会触发的 function 对象

  • set: 一个设置 value 属性时会触发的 function 对象

修改一个属性为只读(readonly)

了解 Object.defineProperty 的基本语法后,我通过它先简单实现一个 readonly 实例。具体代码如下:

Decorator 的基本语法与使用

``javascript
# 定义
function myDecoration(target, name, descriptor) {}

# 对property使用
 class A {
 @myDecorator
 test() {}
 }

 # 对class使用
 @myDecorator
 class A {}

 # 带参数
 function myDescorator(a) {
 return function (target, name, descriptor) {
 console.llog('params:', a)
 }
 }
 @myDescorator(a) 
 class A {}

 # 时使用多个装饰器(Decorator)
 @myDecorator1
 @myDecorator2
 class A {}

利用 Decorator 语法糖修改一个属性为只读(readonly)

利用 Decorator 给 React 组件封装 PureRender

我们都知道,在 React 生命周期里有一个 shouldComponentUpdate 方法,该方法通过返回 ture 或者 false 来确定组件是否重新 render 组件。也就是说,通过该方法我们可以过滤掉些无效的数据渲染事件,从而提升性能。例如我们针对 props 传递过来的数据对象进行对比,如果 props 对象的属性以及值并未变更的情况下,则无需执行render方法。

显然通过对比 props 下数据对象的属性与值是否变更,这种逻辑是可以复用的,而不是在单独的在每个组件中去在重复的写 shouldComponentUpdate 方法。说到改变组件对象的方法行为,这里我们显然就可以使用

Decorator 来这个特性来做了,即我们对应用 Decorator 对象的 shouldComponentUpdate 进行重写。通过遍历 props 对象的属性和值,并与老 props 的属性与值进行对比,从而确定是否需要重新渲染。具体代码如下:

function isEqual(a, b) {
  for (const key in a) {
    if ({}.hasOwnProperty.call(a, key) &&
      (!{}.hasOwnProperty.call(b, key) || a[key] !== b[key])) {
      return false;
    }
  }
  for (const key in b) {
    if ({}.hasOwnProperty.call(b, key) && !{}.hasOwnProperty.call(a, key)) {
      return false;
    }
  }
  return true;
}

export default function pureRender(targetComponent) {
  targetComponent.prototype.shouldComponentUpdate = function (props, state) {
    return !isEqual(this.state, state) || !isEqual(this.props, props)
  }
}

// 使用
@pureRender
class ComponentA extends React.Component {}

通过 Babel 使用 Decorator

由于 Decorator 是ES7中的草案,所以现在需要通过 Bable 才能使用。使用方法如下:

安装

npm install --save-dev babel-plugin-transform-decorators

使用

方法一、 通过配置.babelrc

{
  "plugins": ["transform-decorators"]
}

方法二、通过CLI

babel --plugins transform-decorators script.js

方法三、通过Node API

require("babel-core").transform("code", {
  plugins: ["transform-decorators"]
});

总结

通过 Decorator 这种不需要直接在对象或者方法中编写额外逻辑的方式,就可以轻易的扩展对象或者方法的能力,既满足了功能需求,也精简了代码,保证了代码的可维护性,例如我们已经常见的@log, @test, @mixin等等工具类。所以,以后的工作中可以多多尝试。

 

参考

 

来自:http://div.io/topic/2063

 

扩展阅读

TypeScript 中的 Decorator & 元数据反射:从小白到专家(部分 IV)
Python 与 Javascript 之比较
Python 与 Javascript 之比较
JavaScript的原型和原型链及项目实战
Github资源收集

为您推荐

我的前端故事----React算法又是个什么鬼?
Vue.js基本语法的介绍
给 JavaScript 初心者的 ES2015 实战
前端之React实战-交互与动态UI
React 入门实例教程

更多

JavaScript
JavaScript开发
相关文档  — 更多
相关经验  — 更多
相关讨论  — 更多