用于ES6课程的Mixins,用babel编译

got*_*itz 10 javascript traits composition mixins ecmascript-6

根据各种来源(2ality,esdiscuss),应该能够将mixins添加到类中:

编辑发现类方法不可枚举,因此无法工作.编辑了下面的代码,但仍然没有快乐

class CartoonCharacter {
  constructor(author) {
    this.author = author;
  }

  drawnBy() {
    console.log("drawn by", this.author);
  }
}

// THIS CANNOT WORK
// class methods are not enumerable
// class Human {
//  haveFun() {
//    console.log("drinking beer");
//  }
// }

let Human = Object.create({}, {
  haveFun:   {
    enumerable: true,
    value: function () {
      console.log("drinking beer");
    }
  }
});

class Simpson extends Object.assign(CartoonCharacter, Human) {
  constructor(author) {
    super(author);
  }
}


let homer = new Simpson("Matt Groening");
homer.drawnBy();  // expected: drawn by Matt Groening
homer.haveFun();  // expected: drinking beer
Run Code Online (Sandbox Code Playgroud)

我得到了"由Matt Groening绘制"但不是"喝啤酒"我得到了一个错误

-> Uncaught TypeError: E.haveFun is not a function
Run Code Online (Sandbox Code Playgroud)

小智 16

Subclass Factory Mixins

还有另一种在Javascript中实现mixins的方法:使用子类工厂.

子类工厂是一个除了基类并返回此基类的扩展子类的函数:

const mixin = base => class extends base {
  /* properties to mix in */
}
Run Code Online (Sandbox Code Playgroud)

子类工厂可能有两个原因:

  • classes可以定义为表达式,是Javascript中的第一类*
  • extends 子句可以包含任意表达式

让我们应用这种模式:

// superclass
class CartoonCharacter {
  constructor(author) { this.author = author }
  drawnBy() { return "drawn by " + this.author }
}

// mixin 1
const Human = base => class extends base {
  haveFun() { return "drinking beer" }
}

// mixin 2
const Simpson = base => class extends base {}

// composed subclass
const Homer = Simpson(Human(CartoonCharacter));

// create an instance
const homer = new Homer("Matt Groening")

console.log(homer.drawnBy());
console.log(homer.haveFun());
Run Code Online (Sandbox Code Playgroud)

sublcass工厂的mixins有什么优势?

  • 自然属性优先级:稍后混合的属性将覆盖同名的先前属性
  • mixins(Human/ Simpson)和subclasses(Homer)都可以super照常使用
  • mixins可以有构造函数(有状态混合)
  • 没有原型或实例的变异

有状态的混合

国家让事情变得艰难.你应该尽可能避免国家.但是,有时你的mixin需要它自己的状态,它通过构造函数传递:

class CartoonCharacter {
  constructor(author) { this.author = author }
  drawnBy() { return "drawn by " + this.author }
}

const Human = base => class extends base {
  haveFun() { return "drinking beer" }
}

const Simpson = base => class extends base {
  constructor(arg, ...superArgs) {
    super(...superArgs);
    this.name = arg;
  }
  sayHey() { return "Hey, I am " + this.name }
}

const Homer = Simpson(Human(CartoonCharacter));
const homer = new Homer("Homer Simpson", "Matt Groening")

console.log(homer.drawnBy());
console.log(homer.haveFun());
console.log(homer.sayHey());
Run Code Online (Sandbox Code Playgroud)

*术语第一类函数意味着函数可以作为参数传递或返回值,如普通数据


Aad*_*hah 14

你的mixins有两个问题:

  1. Object.assign仅复制对象的可枚举属性.但是,类的方法和属性是不可枚举的.
  2. 类的方法和属性未在构造函数上定义.它们是在构造函数的原型上定义的.

这是使用mixins扩展类的方法:

class CartoonCharacter {
  constructor(author) {
    this.author = author;
  }

  drawnBy() {
    console.log("drawn by", this.author);
  }
}

class Human {
  haveFun() {
    console.log("drinking beer");
  }
}

mixin(CartoonCharacter, Human);

class Simpson extends CartoonCharacter {
  constructor(author) {
    super(author);
  }
}


let homer = new Simpson("Matt Groening");
homer.drawnBy();  // expected: drawn by Matt Groening
homer.haveFun();  // expected: drinking beer

function mixin(target, source) {
  target = target.prototype; source = source.prototype;

  Object.getOwnPropertyNames(source).forEach(function (name) {
    if (name !== "constructor") Object.defineProperty(target, name,
      Object.getOwnPropertyDescriptor(source, name));
  });
}
Run Code Online (Sandbox Code Playgroud)

它在Babel:demo中按预期工作.