开篇概述

在上篇的ES7之Decorators实现AOP示例中,我们预先体验了ES7的Decorators,虽然它只是一个简单的日志AOP拦截Demo。但它也足以让我们体会到ES7 Decorators的强大魅力所在。所以为什么博主会为它而专门写作此文。在Angular2中的TypeScript Annotate就是标注装潢器的另一类实现。同样如果你也是一个React的爱好者,你应该已经发现了redux2中也开始利用ES7的Decorators进行了大量重构。

尝试过Python的同学们,我相信你做难忘的应该是装潢器。由Yehuda Katz提出的decorator模式,就是借鉴于Python的这一特性。作为读者的你,可以从上一篇博文ES7之Decorators实现AOP示例中看到它们之间的联系。

Decorators

背后原理

ES7的Decorators让我们能够在设计时对类、属性等进行标注和修改成为了可能。Decorators利用了ES5的

Object.defineProperty(target, name, descriptor);

来实现这一特性。如果你还不了解Object.defineProperty,请参见MDN文档。首先我们来考虑一个普通的ES6类:

class Person {
name() { return `${this.first} ${this.last}` }
}

执行这一段class,给Person.prototype注册一个name属性,粗略的和如下代码相似:

Object.defineProperty(Person.prototype, 'name', {
value: specifiedFunction,
enumerable: false,
configurable: true,
writable: true
});

如果利用装潢器来标注一个属性呢?

class Person {
@readonly
name() { return `${this.first} ${this.last}` }
}

在这种装潢下的属性,则会在利用Object.defineProperty为Person.prototype注册name属性之前,执行这段装潢器:

let descriptor = {
value: specifiedFunction,
enumerable: false,
configurable: true,enumerable、
writable: true
}; descriptor = readonly(Person.prototype, 'name', descriptor) || descriptor;
Object.defineProperty(Person.prototype, 'name', descriptor);

从上面的伪代码中,我们能看出,装潢器只是在Object.defineProperty为Person.prototype注册属性之前,执行一个装饰函数,其属于一类对Object.defineProperty的拦截。所以它和Object.defineProperty具有一致的方法签名,它们的3个参数分别为:

  1. obj:作用的目标对象;
  2. prop:作用的属性名;
  3. descriptor: 针对该属性的描述符。

这里最重要的是descriptor这个参数,它是一个数据或访问器的属性描述对象。在对数据和访问器属性描述时,它们都具有configurable、enumerable属性可用。而在数据描述时,value、writable属性则是数据所特有的。get、set属性则是访问器属性描述所特有的。属性描述器中的属性决定了对象prop属性的一些特性。比如 enumerable,它决定了目标对象是否可被枚举,能够在for…in循环中遍历到,或者出现在Object.keys法的返回值中;writable则决定了目标对象的属性是否可以被更改。完整的属性描述,请参见MDN文档

对于descriptor中的属性,它们可以被我们在Decorators中使用,或者修改的,以达到我们标注或者拦截的目的。这也是装潢器拦截的主体信息。

作用于访问器

装潢器也可以作用与属性的getter/setter访问器之上,如下将属性标注为不可枚举的代码:

class Person {
@nonenumerable
get kidCount() { return this.children.length; }
} function nonenumerable(target, name, descriptor) {
descriptor.enumerable = false;
return descriptor;
}

下面是一个更复杂的对访问器的备用录模式运用:

class Person {
@memoize
get name() { return `${this.first} ${this.last}` }
set name(val) {
let [first, last] = val.split(' ');
this.first = first;
this.last = last;
}
} let memoized = new WeakMap();
function memoize(target, name, descriptor) {
let getter = descriptor.get, setter = descriptor.set; descriptor.get = function() {
let table = memoizationFor(this);
if (name in table) { return table[name]; }
return table[name] = getter.call(this);
} descriptor.set = function(val) {
let table = memoizationFor(this);
setter.call(this, val);
table[name] = val;
}
} function memoizationFor(obj) {
let table = memoized.get(obj);
if (!table) { table = Object.create(null); memoized.set(obj, table); }
return table;
}

作用域类上

同样Decorators也可以为class装潢,如下对类是否annotated的标注:

// A simple decorator
@annotation
class MyClass { } function annotation(target) {
// Add a property on target
target.annotated = true;
}

也可以是一个工厂方法

对于装潢器来说,它同样也可以是一个工厂方法,接受配置参数信息,并返回一个应用于目标函数的装潢函数。如下例子,对类可测试性的标记:

@isTestable(true)
class MyClass { } function isTestable(value) {
return function decorator(target) {
target.isTestable = value;
}
}

同样工厂方法,也可以被应用于属性之上,如下对可枚举属性的配置:

class C {
@enumerable(false)
method() { }
} function enumerable(value) {
return function (target, key, descriptor) {
descriptor.enumerable = value;
return descriptor;
}
}

同样在上篇ES7之Decorators实现AOP示例中对于日志拦截的日志类型配置信息,也是利用工厂方法来实现的。它是一个更复杂的工厂方式的Decorators实现。

后续

如上一篇博问所说:虽然它是ES7的特性,但在Babel大势流行的今天,我们可以利用Babel来使用它。我们可以利用Babel命令行工具,或者grunt、gulp、webpack的babel插件来使用Decorators。

关于ES7 Decorators的更有意思的玩法,你可以参见牛人实现的常用的Decorators:core-decorators。以及raganwald如何用Decorators来实现Mixin

细说ES7 JavaScript Decorators的更多相关文章

  1. ES7之Decorators实现AOP示例

    在上篇博文CoffeeScript实现Python装潢器中,笔者利用CoffeeScript支持的高阶函数,以及方法调用可省略括符的特性,实现了一个类似Python装潢器的日志Demo.这只是一种伪实 ...

  2. JavaScript Decorators 的简单理解

    Decorators,装饰器的意思, 所谓装饰就是对一个物件进行美化,让它变得更漂亮.最直观的例子就是房屋装修.你买了一套房子,但是毛坯房,你肯定不想住,那就对它装饰一下,床,桌子,电视,冰箱等一通买 ...

  3. [Javascript] Decorators in JavaScript

    First, what is 'High Order function', basic just a function, inside the function return another fuct ...

  4. Decorator:从原理到实践

    前言 原文链接:Nealyang/personalBlog ES6 已经不必在过多介绍,在 ES6 之前,装饰器可能并没有那么重要,因为你只需要加一层 wrapper 就好了,但是现在,由于语法糖 c ...

  5. Javascript 装饰器极速指南

    pablo.png Decorators 是ES7中添加的JavaScript新特性.熟悉Typescript的同学应该更早的接触到这个特性,TypeScript早些时候已经支持Decorators的 ...

  6. es7你都懂了吗?今天带你了解es7的神器decorator

    es7带来了很多更强大的方法,比如async/await,decorator等,相信大家对于async/await已经用的很熟练了,下面我们来讲一下decorator. 何为decorator? 官方 ...

  7. JavaScript 的装饰器:它们是什么及如何使用

    请访问我的独立博客地址:https://imsense.site/2017/06/js-decorator/ 装饰器的流行应该感谢在Angular 2+中使用,在Angular中,装饰器因TypeSc ...

  8. 大话immutable.js

    为啥要用immutable.js呢.毫不夸张的说.有了immutable.js(当然也有其他实现库)..才能将react的性能发挥到极致!要是各位看官用过一段时间的react,而没有用immutabl ...

  9. 前端解读面向切面编程(AOP)

    前言 面向对象(OOP)作为经典的设计范式,对于我们来说可谓无人不知,还记得我们入行起始时那句经典的总结吗-万事万物皆对象. 是的,基于OOP思想封装.继承.多态的特点,我们会自然而然的遵循模块化.组 ...

随机推荐

  1. phpcms V9 整合 Discuz! X2 教程

    整合原理: UCenter 作服务端:phpsso 与 Discuz! 分别作 UCenter 的客户端应用:phpsso 与 Discuz! 通过 UCenter 发生交互. phpcms 通过 p ...

  2. 用python读写excel(xlrd、xlwt)

    最近需要从多个excel表里面用各种方式整理一些数据,虽然说原来用过java做这类事情,但是由于最近在学python,所以当然就决定用python尝试一下了.发现python果然简洁很多.这里简单记录 ...

  3. Javascript实现的数组降维——维度不同,怎么谈恋爱

    数组的元素可能是数组,这样一层层嵌套,可能得到一个嵌套很深的数组,数组降维要做的事就是把嵌套很深的数组展开,一般最后得到一个一维数组,其中的元素都是非数组元素,比如数组[1, [2, 3, [4, 5 ...

  4. ExtJS客户端代理

    代理(proxy)分为两大类:客户端代理和服务器端代理.客户端代理主要完成与浏览器本地存取数据相关的工作,服务器端代理则是通过发送请求,从服务器端获取数据.根据各自获取数据的方式,客户端代理和服务器端 ...

  5. OpenCv遍历图像小结

    参考:http://www.cnblogs.com/ronny/p/opencv_road_2.html http://blog.csdn.net/xiaowei_cqu/article/detail ...

  6. 【Mybatis架构】Mapper映射文件中的#{}与${}

    前言 还记得当初从北京回来的时候,跟着倪文杰师姐做JavaITOO的一卡通模块,我亲姐贾梦洁带着我一块做,期间,我遇到了一个特别奇葩的问题,就死我要实现Mybatis的模糊查询,根据当时亲姐教给我方法 ...

  7. jquery函数理解与运用

    javascript中有多种不用的方式去理解函数,函数类似于我们以前学过的数学函数,但是在程序设计中,我是按照下面的方式进行理解: 函数的理解: 函数是一个代码块,内容被包含在函数内,通常我们是把一些 ...

  8. 转载:安装ie driver和chrome driver

    很多同学在使用webdriver的时候总是忘了安装ie driver和chrome driver, 因此在这里简单介绍一下这2个driver的安装方式. IE driver 在新版本的webdrive ...

  9. SilverLight抛出 System.InvalidOperationException: 超出了2083 的最大URI

    在SilverLight中对于抛出 System.InvalidOperationException: 超出了 2083 的最大 URI 长度 的异常 处理 其实很简单 在 EntityFramewo ...

  10. 从零开始山寨Caffe·伍:Protocol Buffer简易指南

    你为Class外访问private对象而苦恼嘛?你为设计序列化格式而头疼嘛? ——欢迎体验Google Protocol Buffer 面向对象之封装性 历史遗留问题 面向对象中最矛盾的一个特性,就是 ...