使用 ES.later 的装饰器作为 mixin

原文链接:http://raganwald.com/2015/06/26/decorators-in-es7.html

在函数式 mixin 中,我们讨论了将功能糅合进 JavaScript 类中,从而改变类。我们发现这种方式对于已经在现有代码中使用的类来说存在缺陷,但是对于从头构建一个全新的类不失为一个绝好的技术。当把 mixin 用于类的创建时,mixin 可以使我们得以将类的功能分解成更小的单元,每个小单元专注于自己的事,并按需在多个类之间共享。

让我们回顾一下生成函数式 mixin 的辅助函数,姑且叫它 mixin:

function mixin (behaviour, sharedBehaviour = {}) {
  const instanceKeys = Reflect.ownKeys(behaviour);
  const sharedKeys = Reflect.ownKeys(sharedBehaviour);
  const typeTag = Symbol(‘isa‘);

  function _mixin (target) {
    for (let property of instanceKeys)
      Object.defineProperty(target, property, { value: behaviour[property] });
    Object.defineProperty(target, typeTag, { value: true });
    return target;
  }
  for (let property of sharedKeys)
    Object.defineProperty(_mixin, property, {
      value: sharedBehaviour[property],
      enumerable: sharedBehaviour.propertyIsEnumerable(property)
    });
  Object.defineProperty(_mixin, Symbol.hasInstance, {
    value: (i) => !!i[typeTag]
  });
  return _mixin;
}

上面的 mixin 函数返回一个函数,用于将行为糅合进目标对象,该目标对象可以是类原型或者一个独立的对象。上面的代码片段还提供了一个便利功能,用于给返回的 _mixin 函数自身添加静态的、多实例共享的属性,甚至对 hasInstance 做了简单处理,以便 instanceof 操作符能正常工作。

下面我们将它应用在类的原型上:

const BookCollector = mixin({
  addToCollection (name) {
    this.collection().push(name);
    return this;
  },
  collection () {
    return this._collected_books || (this._collected_books = []);
  }
});

class Person {
  constructor (first, last) {
    this.rename(first, last);
  }
  fullName () {
    return this.firstName + " " + this.lastName;
  }
  rename (first, last) {
    this.firstName = first;
    this.lastName = last;
    return this;
  }
};

BookCollector(Person.prototype);

const president = new Person(‘Barak‘, ‘Obama‘)

president
  .addToCollection("JavaScript Allongé")
  .addToCollection("Kestrels, Quirky Birds, and Hopeless Egocentricity");

president.collection()
  //=> ["JavaScript Allongé","Kestrels, Quirky Birds, and Hopeless Egocentricity"]

只针对类的 mixin

我们的 mixin 能支持任何目标对象,这点很不错,不过我们也可以使其只针对类:

function mixin (behaviour, sharedBehaviour = {}) {
  const instanceKeys = Reflect.ownKeys(behaviour);
  const sharedKeys = Reflect.ownKeys(sharedBehaviour);
  const typeTag = Symbol(‘isa‘);

  function _mixin (clazz) {
    for (let property of instanceKeys)
      Object.defineProperty(clazz.prototype, property, {
        value: behaviour[property],
        writable: true
      });
    Object.defineProperty(clazz.prototype, typeTag, { value: true });
    return clazz;
  }
  for (let property of sharedKeys)
    Object.defineProperty(_mixin, property, {
      value: sharedBehaviour[property],
      enumerable: sharedBehaviour.propertyIsEnumerable(property)
    });
  Object.defineProperty(_mixin, Symbol.hasInstance, {
    value: (i) => !!i[typeTag]
  });
  return _mixin;
}

这一版的 _mixin 函数将实例行为糅合进类的原型,虽然没有支持任何对象那般的灵活,但是使用起来更方便:

const BookCollector = mixin({
  addToCollection (name) {
    this.collection().push(name);
    return this;
  },
  collection () {
    return this._collected_books || (this._collected_books = []);
  }
});

class Person {
  constructor (first, last) {
    this.rename(first, last);
  }
  fullName () {
    return this.firstName + " " + this.lastName;
  }
  rename (first, last) {
    this.firstName = first;
    this.lastName = last;
    return this;
  }
};

BookCollector(Person);

const president = new Person(‘Barak‘, ‘Obama‘)

president
  .addToCollection("JavaScript Allongé")
  .addToCollection("Kestrels, Quirky Birds, and Hopeless Egocentricity");

president.collection()
  //=> ["JavaScript Allongé","Kestrels, Quirky Birds, and Hopeless Egocentricity"]

至此,非常不错,但总觉得有点马后炮的感觉。我们可以利用一下“类即表达式”这一事实:

const BookCollector = mixin({
  addToCollection (name) {
    this.collection().push(name);
    return this;
  },
  collection () {
    return this._collected_books || (this._collected_books = []);
  }
});

const Person = BookCollector(class {
  constructor (first, last) {
    this.rename(first, last);
  }
  fullName () {
    return this.firstName + " " + this.lastName;
  }
  rename (first, last) {
    this.firstName = first;
    this.lastName = last;
    return this;
  }
});

这样一来在结构上更漂亮了,因为我们将行为糅合这一过程和类声明写在了一个表达式里,就不会看起来像先创建类,再将其他东西添加到类里一样。

然而(总是能找到不完美之处),我们的模式具有三个不同的元素(被绑定的变量名,mixin,以及声明的类)。如果我们希望糅合进更多功能,不得不像这样嵌套函数调用:

const Author = mixin({
  writeBook (name) {
    this.books().push(name);
    return this;
  },
  books () {
    return this._books_written || (this._books_written = []);
  }
});

const Person = Author(BookCollector(class {
  // ...
}));

有些人觉得这非常明了,因为他们觉得如此简单的表达式充分体现了 JavaScript 的简洁性。并且 mixin 的内部实现简单易读,只要你理解原型,便能理解该表达式的含义。

但另外一些人希望编程语言能给他们提供“救赎”,他们只需单独学习语法带来的抽象。目前,JavaScript 还没有将功能糅合进类的相关“魔法”。但如果有的话会是什么样呢?

类装饰器

其实在 ECMAScript 2015 之后的对 JavaScript 的主要修订中,有一个备受瞩目的提案——将 python 风格的类装饰器加入 JavaScript 中。

装饰器就是一个作用于类的函数。这里有一个例子,其所基于的装饰器实现符合上述提案的要求:

function annotation(target) {
   // Add a property on target
   target.annotated = true;
}

@annotation
class MyClass {
  // ...
}

MyClass.annotated
  //=> true

上例中,annotation 是一个类装饰器,它接受一个类作为参数。该装饰器函数可以做任何事情,包括修改类本身或类的原型。如果装饰器函数没有返回值,参数类的名字就被绑定到修改后的类。

想让某个类被函数“修饰”,只需要在某个表达式前添加 @ 符号,而该表达式需要被解读成装饰器函数。

O__O “…,你是说能够修改类的函数吗?那我们就试试吧:

const BookCollector = mixin({
  addToCollection (name) {
    this.collection().push(name);
    return this;
  },
  collection () {
    return this._collected_books || (this._collected_books = []);
  }
});

@BookCollector
class Person {
  constructor (first, last) {
    this.rename(first, last);
  }
  fullName () {
    return this.firstName + " " + this.lastName;
  }
  rename (first, last) {
    this.firstName = first;
    this.lastName = last;
    return this;
  }
};

const president = new Person(‘Barak‘, ‘Obama‘)

president
  .addToCollection("JavaScript Allongé")
  .addToCollection("Kestrels, Quirky Birds, and Hopeless Egocentricity");

president.collection()
  //=> ["JavaScript Allongé","Kestrels, Quirky Birds, and Hopeless Egocentricity"]

你也可以使用装饰器添加多个行为:

const BookCollector = mixin({
  addToCollection (name) {
    this.collection().push(name);
    return this;
  },
  collection () {
    return this._collected_books || (this._collected_books = []);
  }
});

const Author = mixin({
  writeBook (name) {
    this.books().push(name);
    return this;
  },
  books () {
    return this._books_written || (this._books_written = []);
  }
});

@BookCollector @Author
class Person {
  constructor (first, last) {
    this.rename(first, last);
  }
  fullName () {
    return this.firstName + " " + this.lastName;
  }
  rename (first, last) {
    this.firstName = first;
    this.lastName = last;
    return this;
  }
};

如果你想使用装饰器模拟“纯函数式复合”,这也是简单易行的惯常模式:

class Person {
  constructor (first, last) {
    this.rename(first, last);
  }
  fullName () {
    return this.firstName + " " + this.lastName;
  }
  rename (first, last) {
    this.firstName = first;
    this.lastName = last;
    return this;
  }
};

@BookCollector @Author
class BookLover extends Person {};

类装饰器提供了一个紧凑的、魔法般的语法,该语法和类的创建紧密关联。它的引入确实需要你学习新的语法,但不同的语法实现不同的事情让代码理解起来更容易,比如 @foo 表示装饰器,bar(…) 表示函数调用,这不失为成功之举。

使用装饰器

装饰器提案尚未被正式通过,然而将装饰器语法转译成 ES5 语法存在多种可用实现。这篇文章中的例子都是使用 Babel 转译的。

如果你倾向于使用语法糖让代码具有声明式编程的形态,那就将 mixin 函数和 ES.later 的类装饰器结合起来使用吧。

原文地址:https://www.cnblogs.com/averyby/p/10027793.html

时间: 2024-10-16 05:13:37

使用 ES.later 的装饰器作为 mixin的相关文章

用户登陆装饰器和页面跳转(包含mixin的使用)

用login_required装饰器来辨别用户是否登陆,如果没有登陆,则需要进行登录才能继续访问当前页面(有些页面是需要用户登陆后才可以访问):如果已经登陆,则可以继续访问. 首先配置 如果用户没有登陆,login_required装饰器会默认一个地址进行跳转,所以要在settings中进行配置需要跳转的页面: 在user.views中配置: 若检查到没有登陆让用户登陆默认链接时,此时登陆页面链接为,上图中的get里'next'指的就是登陆页面里next对应的页面,后边的则是为若链接中没有nex

Javascript装饰器的妙用

最近新开了一个Node项目,采用TypeScript来开发,在数据库及路由管理方面用了不少的装饰器,发觉这的确是一个好东西.装饰器是一个还处于草案中的特性,目前木有直接支持该语法的环境,但是可以通过 babel 之类的进行转换为旧语法来实现效果,所以在TypeScript中,可以放心的使用@Decorator. 什么是装饰器 装饰器是对类.函数.属性之类的一种装饰,可以针对其添加一些额外的行为.通俗的理解可以认为就是在原有代码外层包装了一层处理逻辑.个人认为装饰器是一种解决方案,而并非是狭义的@

ES6 系列之我们来聊聊装饰器

Decorator 装饰器主要用于: 装饰类 装饰方法或属性 装饰类 @annotation class MyClass { } function annotation(target) { target.annotated = true; } 装饰方法或属性 class MyClass { @readonly method() { } } function readonly(target, name, descriptor) { descriptor.writable = false; retu

Day4 - 迭代器&生成器、装饰器、Json & pickle 数据序列化、软件目录结构规范

---恢复内容开始--- 本节内容 迭代器&生成器 装饰器 Json & pickle 数据序列化 软件目录结构规范 作业:ATM项目开发 1.列表生成式,迭代器&生成器 列表生成式 需求:列表a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],要求把列表里的每个值加1 1 a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 2 b = [] 3 for i in a: 4 b.append(i+1) 5 a = b 6 print(a) 普通青

python3 装饰器

看廖雪峰官网的python3装饰器有感 装饰器即将一个函数作为变量在新的函数中调用此函数. 作业: 能否写出一个@log的decorator,使它既支持: @logdef f():     pass 又支持: @log('execute')def f():     pass      例1: import functools import time def log(*args,**kwargs):     # *args 是个元组     if args and isinstance(args,

day14 带函数的装饰器、多个装饰器装饰一个函数

一.带参数的装饰器:------开关 __author__ = 'Administrator' F=True def outer(F): def wap(fun):#gg def inner(*args,**kwargs): if F: print("inner before") ret=fun(*args,**kwargs)#gg() print("inner after") else: ret=fun(*args,**kwargs) return ret ret

Python 装饰器

一.定义 器即函数 装饰即修饰,意指为其他函数添加新功能 装饰器本身可以是任意可调用对象,被装饰的对象本身也可以是任意可调用对象 实现装饰器: 装饰器=高阶函数+函数嵌套+闭包 二.原则: 1 .开放封闭原则:对扩展是开放的,对修改是封闭 2.1 装饰器的遵循的原则:1 不修改被装饰对象的源代码 2 不修改被调用对象的调用方式 三.目的 装饰器的目的是:在遵循1和2原则的前提,为其他新功能函数添加 四.定义位置 @装饰器名,必须写在被装饰对象的正上方,并且是单独一行 import time de

装饰器、生成器、迭代器

装饰器的前奏 装饰器:本质是函数 功能:就是装饰成其他函数  就是为其他函数添加附加功能的 高阶函数+嵌套函数=装饰器 原则:1.不能修改被装饰的函数的源代码 2.不能修改被装饰的函数的调用方式 总结一句话:装饰器对被装饰的函数是完全透明的 实现装饰器的只是储备: 1.函数名即"变量"   将函数体赋值给变量   和内存回收机制一样 2.高阶函数 2.1.把函数名作为实参传递给形参(可返回被修饰函数的地址)(不修改源代码的情况可添加新的功能) 2.2返回值中包含函数地址(不修改函数的调

python 装饰器的用法

为什么要使用装饰器? 在不改变原函数功能的情况,为了添加新的功能 我们可以在函数运行前后给函数添加新的功能 1 def outer(func): 2 #fun()等于原f1函数 3 def inner(): 4 print('ccccc') 5 r=func() 6 print('dddd') 7 return r 8 return inner 9 @outer 10 #@outer代表运行了2个步骤:1.将f1作为参数运行outer函数,2.新函数f1=inner() 11 def f1():