装饰器 (ES7-Decorator)

358 阅读2分钟

ES7-Decorator

装饰器常见用法

react-redux 中的 connect

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { increment, decrement } from '../redux/actions'

@connect(
    state=>({value: state.test}),
    {increment,decrement}
)
class Counter extends Component {
    onIncrease() {
        this.props.increment(5);
    }
    onDecrement() {
        this.props.decrement(3);
    }
    render() {
        const { value } = this.props;
        return (
            <div>
                Test: 测试{value}
                <button onClick={this.onIncrease.bind(this)}>增加</button>
                <button onClick={this.onDecrement.bind(this)}>减少</button>
            </div>
        );
    }
}

export default Counter;

Ant Design 的Form.create

import React, { Component } from 'react';
import { Form } from 'antd';

@Form.create({
    name: 'ingestion_form'
})
class IngestionForm extends Component {
  // 表单组件
}

ES7-Decoratro

仅仅包装现有的模块,使之 “更加华丽” ,并不会影响原有接口的功能 —— 好比你给手机添加一个外壳罢了,并不影响手机原有的通话、充电等功能;

这是网上看到比较形象的比喻,钢铁侠本质是人,但通过外穿的装备获得增强的能力

装饰器是实现面向切面的编程(AOP)编程的一种方式,他可以让我们的程序设计更优雅

它具体做了什么工作 ?它“装饰”了类、方法、访问器、属性和参数——“它”是指类装饰器、方法装饰器、访问器装饰器、属性装饰器和参数装饰器

类的装饰(Class decorators)

@testable
class MyTestableClass {
  // ...
}

function testable(target) {
  target.isTestable = true;
}

MyTestableClass.isTestable // true


// 相当于定义
class MyTestableClass {
  static isTestable = true
}

@testable就是一个装饰器。它修改了MyTestableClass这个类的行为,为它加上了静态属性isTestabletestable函数的参数targetMyTestableClass类本身

基本上,装饰器的行为就是下面这样

@decorator
class A {}

// 等同于

class A {}
A = decorator(A) || A;

扩展参数,可以在装饰器外面在加一层函数(闭包)

function testable(isTestable) {
  return function(target) {
    target.isTestable = isTestable;
  }
}

@testable(true)
class MyTestableClass {}
MyTestableClass.isTestable // true

@testable(false)
class MyClass {}
MyClass.isTestable // false

上面的例子都是添加类型的静态属性,如果想添加实例属性,可以通过target.prototype.value来添加。

方法/属性的装饰(Class property decorators)

看下 ES6 中的类

class Cat {
    say() {
        console.log("meow ~");
    }
}

上面的其实只是个语法糖,实际添加类属性时候,会使用Object.defineProperty这个方法,它接受 3 个参数targetnamedescriptor

function Cat() {}
Object.defineProperty(Cat.prototype, "say", {
    value: function() { console.log("meow ~"); },
    enumerable: false,
    configurable: true,
    writable: true
});

属性/方法装饰器的参数和上面Object.defineProperty使用的参数相同

比如有的时候,我们希望把我们的部分属性置成只读,以避免别人对其进行修改,如果使用装饰器的话,我们可以这样来做:

function readonly(target, name, descriptor) {
    discriptor.writable = false;
    return discriptor;
}

class Cat {
    @readonly
    say() {
        console.log("meow ~");
    }
}

var kitty = new Cat();

kitty.say = function() {
    console.log("woof !");
}

kitty.say()    // meow ~
class Math {
  @log
  add(a, b) {
    return a + b;
  }
}

function log(target, name, descriptor) {
  var oldValue = descriptor.value;

  descriptor.value = function() {
    console.log(`Calling ${name} with`, arguments);
    return oldValue.apply(this, arguments);
  };

  return descriptor;
}

const math = new Math();

// passed parameters should get logged now
math.add(2, 4);

JS 里对于装饰器的描述

A decorator is:

  • an expression
  • that evaluates to a function
  • that takes the target, name, and decorator descriptor as arguments
  • and optionally returns a decorator descriptor to install on the target object
babel --plugins transform-decorators-legacy es6.js > es5.js

参考文档: