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性能优化之编程技巧总结

程序的性能受代码质量的直接影响。在本文中,主要介绍一些代码编写的小技巧和惯例,这些技巧有助于在代码级别上提升系统性能。

611
来自专栏测试开发架构之路

C语言之位运算

指针和位运算很适合编写系统软件的需要。 位运算指进行二进制位的运算。   按位与”运算符 & 用途 1)清零 2)取一个数中某些指定位(比如只需要低8位) 3)...

32610
来自专栏coolblog.xyz技术专栏

红黑树详细分析,看了都说好

红黑树是一种自平衡的二叉查找树,是一种高效的查找树。它是由 Rudolf Bayer 于1978年发明,在当时被称为对称二叉 B 树(symmetric bin...

54921
来自专栏蛋未明的专栏

PHP使用类“单例”静态变量提高效率

1533
来自专栏恰同学骚年

剑指Offer面试题:16.合并两个排序的链表

PS:这也是一道出镜率极高的面试题,我相信很多童鞋都会很眼熟,就像于千万人之中遇见不期而遇的人,没有别的话可说,唯有轻轻地问一声:“哦,原来你也在这里? ”

521
来自专栏老马说编程

(73) 并发容器 - 写时拷贝的List和Set / 计算机程序的思维逻辑

本节以及接下来的几节,我们探讨Java并发包中的容器类。本节先介绍两个简单的类CopyOnWriteArrayList和CopyOnWriteArraySet,...

1956
来自专栏跟着阿笨一起玩NET

深入了解VSTS的Unit Test测试属性

深入的了解一下方法上带有的属性的含义. 每个方法上几乎都带有TestMethod这个属性,我们直觉告诉我们,这肯定是表示被测试函数的意思.事实也正是如此,在Un...

311
来自专栏鸿的学习笔记

Python和Scala的定义变量

每一门的编程语言背后都代表着某一种特别的哲学,由这一哲学进而设计出属于这门程序语言的语法,Python和Scala也不例外。我们从变量的定义去一窥Python和...

802
来自专栏null的专栏

数据结构和算法——Huffman树和Huffman编码

Huffman树是一种特殊结构的二叉树,由Huffman树设计的二进制前缀编码,也称为Huffman编码在通信领域有着广泛的应用。在word2vec模型中,在构...

2666
来自专栏Crossin的编程教室

【每周一坑】神奇的九宫格

五一小长假大家应该玩的挺开心吧,还沉浸在假日的愉悦中么?请大家收收心,准备准备月底的端午节。 看看本周的题目吧,本周的题目由读者 @疯琴 提供,我们做了小小的改...

2657

扫码关注云+社区