kekobin / blog

blog
7 stars 0 forks source link

[译]使用Javascript mixins #3

Open kekobin opened 5 years ago

kekobin commented 5 years ago

译文来自https://alligator.io/js/using-js-mixins/.

当谈到高级类的组合时,Javascript有非常多的方式,那简直就是个大杂烩。其中的一种方式是基于mixin的继承模式,这种方式常常被人所忽略(特别是开发新人),但它确是非常有用的。虽说mixin有时候非常的密集,以至难以书写和理解,但它的确拥有非常多值得研究的特性。

就像它的名字表明的那样,mixin是一种将一个对象和另一些对象混合在一起以添加我们所需要的属性的模式。那就像是给你的对象提供了额外的属性,但是这些独立的属性又不是真正的子类本身。 表面上看来,mixin的行为类似于对象混合层,在这里我们传入目标(mixin)和源。目标将附加到源,并返回一个新对象。

更准确的描述是mixin作为工厂工作,返回新的子类对象。而在整个过程中,任何地方都没有去定义子类。 如果和C++类比的话,就是将它们与具有虚拟函数的抽象类进行比较,允许它们由其他子类继承。 所以,既然我们知道mixin允许我们创建一个修改过的定义,可以应用到现有的超类中来创建新的子类,那么让我们看看mixin是什么样子的:

//The swim property here is the mixin
let swim = {
  location() {
    console.log(`Heading ${this.direction} at ${this.speed}`);
  }
};

let Alligator = function(speed, direction) {
  this.speed = speed,
  this.direction = direction
};

//This is our source object
let alligator = new Alligator('20 mph','North');

alligator = Object.assign(alligator, swim);
console.log(alligator.location());

在上面的片段中,我们要创建一个能游泳的鳄鱼。所以我们new了一个鳄鱼的实例,然后给它添加了游泳功能。swim对象是我们希望鳄鱼对象使用object.assign方法拥有的mixin或扩展。 object.assign方法允许我们一次添加多个mixin。多个mixin的示例就像如下展示的那样:

alligator = Object.assign(alligator, swim, crawl);

现在让我们看看mixin如何与ES6类一起使用:

let swim = {
 setSwimProperties(speed, direction) {
   this.speed = speed;
   this.direction = direction;
 },

 getSwimProperties(){
   console.log(`swimming ${this.speed} towards ${this.direction}`);
 }
}

class Reptile {
 constructor(name) {
   this.name = name;
 }
}

Object.assign(Reptile.prototype, swim);
let alligator = new Reptile("alligator");
alligator.setSwimProperties("5 m/s", "upstream");
alligator.getSwimProperties();

通过mixin方法添加功能的优点是灵活性。mixin是一个非常原始的函数,就像它只做一件事一样,允许我们在各种场景中重复使用这些结构。它可以与本机函数调用一起使用,用于类定义等。 另一个好处是,它倾向于保持类层次结构水平——允许超类使用mixin来创建具有所需子类属性的新对象,而不是使继承链在为这些情况创建新子类时更长(因继承链越长性能越差)。

不过,在使用mixin时要注意几点: 1.object.assign(在对象和类实现中)只执行mixin属性的一个浅拷贝。 2.在使用来自不同mixin的属性时可能会发生名称冲突(多重继承中的菱形问题) 3.因为属性被复制到源对象上,所以很难确定该属性来自哪个mixin。InstanceOf运算符在这里无法帮助我们。