JS 装潢器剖析

《JS 装潢器剖析》

跟着 ES6 和 TypeScript 中类的引入,在某些场景须要在不转变原有类和类属性的基础上扩大些功用,这也是装潢器涌现的缘由。

装潢器简介

作为一种能够动态增删功用模块的形式(比方 redux 的中间件机制),装潢器一样具有很强的动态灵活性,只需在类或类属性之前加上 @要领名 就完成了响应的类或类要领功用的变化。

不过装潢器形式仍处于第 2 阶段提案中,运用它之前须要运用 babel 模块 transform-decorators-legacy 编译成 ES5 或 ES6。

在 TypeScript 的 lib.es5.d.ts 中,定义了 4 种差别装潢器的接口,个中装潢类以及装潢类要领的接口定义以下所示:

declare type ClassDecorator = <TFunction extends Function>(target: TFunction) => TFunction | void;
declare type MethodDecorator = <T>(target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<T>) => TypedPropertyDescriptor<T> | void;

下面临这两种状况举行剖析。

作用于类的装潢器

当装潢的对象是类时,我们操纵的就是这个类自身

@log
class MyClass { }

function log(target) { // 这个 target 在这里就是 MyClass 这个类
   target.prototype.logger = () => `${target.name} 被挪用`
}

const test = new MyClass()
test.logger() // MyClass 被挪用

因为装潢器是表达式,我们也能够在装潢器背面再增加提个参数:

@log('hi')
class MyClass { }

function log(text) {
  return function(target) {
    target.prototype.logger = () => `${text},${target.name} 被挪用`
  }
}

const test = new MyClass()
test.logger() // hello,MyClass 被挪用

在运用 redux 中,我们最常运用 react-redux 的写法以下:

@connect(mapStateToProps, mapDispatchToProps)
export default class MyComponent extends React.Component {}

经由上述剖析,我们知道了上述写法等价于下面这类写法:

class MyComponent extends React.Component {}
export default connect(mapStateToProps, mapDispatchToProps)(MyComponent)

作用于类要领的装潢器

与装潢类差别,对类要领的装潢实质是操纵其描述符。能够把此时的装潢器明白成是 Object.defineProperty(obj, prop, descriptor) 的语法糖,看以下代码:

class C {
  @readonly(false)
  method() { console.log('cat') }
}

function readonly(value) {
  return function (target, key, descriptor) { // 此处 target 为 C.prototype; key 为 method;
    // 原 descriptor 为:{ value: f, enumarable: false, writable: true, configurable: true }
    descriptor.writable = value
    return descriptor
  }
}

const c = new C()
c.method = () => console.log('dog')

c.method() // cat

能够看到装潢器函数吸收的三个参数与 Object.defineProperty 是完整一样的,详细完成能够看 babel 转化后的代码,重要完成以下所示:

var C = (function() {
  class C {
    method() { console.log('cat') }
  }

  var temp
  temp = readonly(false)(C.prototype, 'method',
    temp = Object.getOwnPropertyDescriptor(C.prototype, 'method')) || temp // 经由过程 Object.getOwnPropertyDescriptor 获取到描述符传入到装潢器函数中

  if (temp) Object.defineProperty(C.prototype, 'method', temp)
  return C
})()

再将再来看看如果有多个装潢器作用于同一个要领上呢?

class C {
  @readonly(false)
  @log
  method() { }
}

经 babel 转化后的代码以下:

desc = [readonly(false), log]
    .slice()
    .reverse()
    .reduce(function(desc, decorator) {
      return decorator(target, property, desc) || desc;
    }, desc);

能够清楚地看出,经由 reverse 倒序后,装潢器要领会至里向外实行。

相干链接

javascript-decorators
Javascript 中的装潢器
JS 装潢器(Decorator)场景实战
润饰器
Babel

    原文作者:牧云云
    原文地址: https://segmentfault.com/a/1190000013825341
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞