decorator 学习小结

decorator 学习小结

1. decorator 是什么

decorator 是装饰者,是 ES7 的语法。

decorator 本质是一个 wrapper,可以动态增强【类】,【实例方法】的能力

被装饰者对于装饰者是毫无感知的

读者如果感兴趣,可以深入学习【装饰者模式】

要注意的是,在 ES7 中,decorator 只是一个语法糖而已,和 class 这些一样

可以参考 babel 转换之后的 ES5 代码

2. decorator 的用法

2.1. 作用在类方法上

decorator 可以在定义类的时候,作用在类方法上,见下面的例子:

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

class People {
    @readonly
    sayName() {
        console.log(this.name);
    }
}

var p = new People();
p.sayName = 'hello';

执行上面的代码,会报错,如下:

decorator 本质是一个 wrapper 函数,当 decorator 作用在类方法的时候

它的签名是function(target, key, descriptor)

  • target: 类原型,也就是 People.prototype
  • key: 属性名,也就是sayName
  • descriptor: 属性的描述符

可以发现,decorator 的签名和 Object.defineProperty 的签名是一样的

本质上来说,作用在类方法的 decorator 就是对 Object.defineProperty 的 wrapper

2.2. 作用在类上

decorator 可以在定义类的时候,作用在类上面,对类进行增强

function nonTestable(target) {
    target.canTest = false;
}

@nonTestable
class People {}

console.log(People.canTest); // false

当 decorator 作用在类上时,只会传入一个参数,就是类本身

2.3. decorator 也可以是工厂函数

当同一个 decorator 在作用在不同目标上面时,有不同的表现时,可以把 decorator 定义为一个工厂函数,详见下面的例子:

function addName(name) {
    return function(target) {
        target.myName = name;
    };
}

@addName('people')
class People {
    static say() {
        console.log(People.myName);
    }
}

@addName('dog')
class Dog {
    static say() {
        console.log(Dog.myName);
    }
}

People.say(); // people
Dog.say(); // dog

3. 如何使用 decorator?

既然是 ES7 的新语法,那么如何使用 decorator?

目前需要使用 babel 来进行转换才能使用,具体方法如下:

.babelrc文件如下:

{
  "presets": ["es2015", "stage-1"],
  "plugins": [
    "babel-plugin-transform-decorators-legacy"
  ]
}

然后需要安装插件:

npm i babel-cli babel-preset-es2015 babel-preset-stage-1 babel-plugin-transform-decorators

安装完成之后,就可以使用 babel 来转换带 decorator 的代码了:

babel index.js > index.es5.js

4. 应用 decorator

实际上,decorator 有非常广泛的运用场景,这里我简单举两个例子

4.1. 利用 decorator 实现 mixin

废话少说,直接举例子:

function nameMixin(target) {
    target.prototype.setName = function(name) {
        this.name = name;
        return this;
    };

    target.prototype.sayName = function() {
        console.log(this.name);
    };
}

function ageMixin(target) {
    target.prototype.setAge = function(age) {
        this.age = age;
        return this;
    };

    target.prototype.sayAge = function() {
        console.log(this.age);
    };
}

@nameMixin
@ageMixin
class People {}

var p = new People();
p.setName('peter').sayName(); // peter
p.setAge(18).sayAge(); // 18

可以对比一下以前的 mixin 使用方式:

class People extends mixin(ageMixin, nameMixin) {}
// or
class People {}
mixin(People, ageMixin, nameMixin);

可以看到,使用 decorator 之后,代码简单明了了许多

4.2. 利用 decorator 实现 AOP

function aop(before, after) {
    return function(target, key, descriptor) {
        const method = descriptor.value;
        descriptor.value = (...args) => {
            let ret;

            before && before(...args);
            ret = method.apply(target, args);
            if (after) {
                ret = after(ret);
            }

            return ret;
        };
    };
}

function beforeTest1(opt) {
    opt.name = opt.name + ', haha';
}

function beforeTest2(...args) {}

function afterTest2(ret) {
    console.log('haha, add 10!');
    return ret + 10;
}

class Test {
    @aop(beforeTest1)
    static test1(opt) {
        console.log(`hello, ${opt.name}`);
    }

    @aop(beforeTest2, afterTest2)
    static test2(...args) {
        return args.reduce((a, b) => a + b);
    }
}

/**
 * out:
 * 
 * hello, peter, haha
 * haha, add 10!
 * 16
 * 
 */
Test.test1({
    name: 'peter'
});
console.log(Test.test2(1, 2, 3));

这里只是实现了一个简单的 aop 例子

大家可以看到,使用了 decorator 之后,代码非常清晰

5. 小结

decorator 是 ES7 的新语法,本质上来说,它就是一个语法糖,但是缺非常有用

任何装饰者模式的代码都可以通过 decorator 来实现

使用了 decorator 之后,代码会变得清晰明了

相信不久的将来,ES7 语法将会流行,但是在这之前,你仍然可以通过 babel 来使用 decorator

快来享受 decorator 带来的简洁吧!

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Java技术栈

switch case 支持的 6 种数据类型!

1494
来自专栏Google Dart

Dart语言指南(二) 顶

Dart是一种面向对象的语言 包含类和基于 mixin 的继承两部分。每个对象是一个类的实例, 并且 Object.是所有类的父类。 基于 mixin 的继承指...

731
来自专栏小勇DW3

承接上文

前置通知[Before advice]:在连接点前面执行,前置通知不会影响连接点的执行,除非此处抛出异常。 

852
来自专栏技术博客

一步一步学Linq to sql(一):预备知识

  Linq to sql(或者叫DLINQ)是LINQ(.NET语言集成查询)的一部分,全称基于关系数据的 .NET 语言集成查询,用于以对象形式管理关系数据...

661
来自专栏盛国存的专栏

A Bite of GoLang(上)

A bite of GoLang(浅尝GoLang),本文只是Go语言的冰山一角,本文包含作者学习Go语言期间积累的一些小的经验,同时为了方便让读者了解到Go语...

50410
来自专栏java沉淀

java注解的工作原理及使用场景(Demo小记)

1182
来自专栏码神联盟

碎片化 | 第四阶段-23-自定义注解-视频

如清晰度低,可转PC网页观看高清版本: http://v.qq.com/x/page/g05026wfgfl.html 注解 注解是附加到代码中的一些元信息...

34810
来自专栏流柯技术学院

TestNG官方文档中文版(2)-annotation

TestNG的官方文档的中文翻译版第二章,原文请见 http://testng.org/doc/documentation-main.html 2 - Ann...

861
来自专栏Jimoer

java设计模式之代理模式

代理模式 代理模式是常见设计模式的一种,代理模式的定义是:为其他对象提供一种代理以控制对这个对象的访问。 在某些情况下,一个对象不适合或者不能直接引用另一个对象...

3055
来自专栏拭心的安卓进阶之路

Java 进阶巩固:什么是注解以及运行时注解的使用

这篇文章 2016年12月13日星期二 就写完了,当时想着等写完另外一篇关于自定义注解的一起发。结果没想到这一等就是半年多 - -。 有时候的确是这样啊,总想着...

1815

扫码关注云+社区