博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
细说ES7 JavaScript Decorators
阅读量:6910 次
发布时间:2019-06-27

本文共 3650 字,大约阅读时间需要 12 分钟。

开篇概述

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

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

Decorators

背后原理

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

Object.defineProperty(target, name, descriptor);

来实现这一特性。如果你还不了解Object.defineProperty,请参见。首先我们来考虑一个普通的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则决定了目标对象的属性是否可以被更改。完整的属性描述,请参见。

对于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@annotationclass 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;  }}

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

后续

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

关于ES7 Decorators的更有意思的玩法,你可以参见牛人实现的常用的Decorators:。以及的。

本文转自破狼博客园博客,原文链接:http://www.cnblogs.com/whitewolf/p/details-of-ES7-JavaScript-Decorators.html,如需转载请自行联系原作者

你可能感兴趣的文章
好久没做.Net开发了,今天配置IIS和.Net Framework 4.0遇到点问题
查看>>
emoji情感分类器
查看>>
简单理解java反射机制
查看>>
Codeforces 399B - Red and Blue Balls
查看>>
实验五
查看>>
Qt资源
查看>>
n个骰子的点数
查看>>
Linux硬盘扩容(非LVM)
查看>>
glibc malloc常驻内存不释放问题抽象
查看>>
insufficient space
查看>>
linux获取CPU温度
查看>>
linux c 链接详解2-定义和声明
查看>>
负载均衡(Load Balancing)学习笔记(三)
查看>>
「一本通 1.1 练习 1」数列极差
查看>>
Event Flow
查看>>
Unity3d插件开发与SDK对接实战 学习
查看>>
Java代码实例 判断这个数是否是素数
查看>>
[原]vue - webapp 返回无效 解决方案
查看>>
FlexSlider插件的详细设置参数
查看>>
Binary Indexed Tree (Fenwick Tree)
查看>>