JavaScript中Mixins和Inheritance之间的实际区别是什么?

3

仅仅模拟多重继承是混合使用的唯一优势吗:

 Object.assign( MyClassA.prototype, MyMixinB )

继承 vs. 组合

 class MyClass extends MyClassB {

 // MyClassB = class version of MyMixinB

在ES6 JavaScript中是什么? 谢谢。

什么是“类的版本”? - Bergi
1个回答

5
不会有一个简短的答案。实际上,JavaScript(JS)为构建类型/对象系统提供了许多不同的技术方法(以及如何将它们组合在一起)。
首先,正如原始贴文中所述,JS 中没有真正/真实的多重继承。这种语言只提供基于原型的单一继承(可以链接),无论是选择类还是传统的纯函数风格。
但其次,与其他编程语言(PL)一样,还有另一种不基于继承的代码重用和对象组合方式...它是按混入(mixin)和/或特征(trait)组合的。
但我将坚持使用 OP 提供的两个示例代码。
Object.assign(MyClassA.prototype, MyMixinB)

这已经是一个不错的例子,如果考虑到MyMixinB作为基于对象的混入提供额外的行为,例如...

var arr = ['object', 'based', 'mixin', 'approach'];
var list = { length: 4, 0: 'the', 1: 'quick', 2: 'brown', 3: 'fox' };

console.log('(typeof arr.last) : ', (typeof arr.last));
console.log('(typeof list.last) : ', (typeof list.last));

const withObjectBasedListLastBehavior = {
  last: function () {
    return this[this.length - 1];
  }
}
Object.assign(Array.prototype, withObjectBasedListLastBehavior);
Object.assign(list, withObjectBasedListLastBehavior);

console.log('(typeof arr.last) : ', (typeof arr.last));
console.log('arr.last() : ', arr.last());

console.log('(typeof list.last) : ', (typeof list.last));
console.log('list.last() : ', list.last());

console.log('(arr.last === list.last) : ', (arr.last === list.last));
console.log('(arr.last === Array.prototype.last) : ', (arr.last === Array.prototype.last));
.as-console-wrapper { max-height: 100%!important; top: 0; }

上述方法是组合和继承的结合体。 withObjectBasedListLastBehavior mixin 为类似列表结构提供了通用实现的 last 方法。这个结构本身不会产生什么影响。通过 Object.assign,可以将 last 列表行为分配给任何类似于列表的结构。但是,在第一个示例中,OP将mixin分配给了一个原型。因此,除非调用没有自己的 last 方法的实例,否则不会使用所获得的新行为。然后进行原型委派...现在的原型last将在调用对象的上下文中被调用。
通过 mixin(具有此类行为的结构)提供附加行为/功能的优点在于它们的灵活性,以及如何重复使用这些结构。理想的 mixin 是原子性的,并且具有一个特定的行为(一个方法)。然后,该行为可以从类构造函数/构造函数的主体内混合,或者如上例中一样,将其分配给任何对象(原型对象或任何其他类型)。因此,在JavaScript中通过mixin / traits实现代码重用在两个级别上是可能的...在类级别(在构建/实例化时)和在任何时间在对象级别。
这种灵活性应该不会让人感到惊讶,因为mixin / traits仅从行为的角度(一个对象具有一种行为)贡献于组成,而实际的继承,在JS中也是负责“是一种事物”的关系。
应该意识到,基于mixin / trait的组合与继承之间的差异并不是JavaScript特定的事情。这些概念也适用于其他PL。从技术上讲,JS更加灵活,因为它是基于对象的,并且确实具有两种委派方式,隐式通过自动运行的原型委派和显式通过传递上下文直接调用函数的callapply方法。
上述提供的强烈意见的实际证明考虑了OP的第二个示例代码...
class MyClass extends MyClassB {

// MyClassB = class version of MyMixinB

... 并将其更改为类似于此类的东西 ...

const withFunctionBasedListLastBehavior = (function () {
  function last() {   // single implementation
    return this[this.length - 1];
  }
  return function() { // always going to share the above
    this.last = last; // single implementation, thus always
  };                  // featuring one and the same signature.
}());

const withFunctionBasedListFirstBehavior = (function () {
  function first() {
    return this[0];
  }
  return function() {
    this.first = first;
  };
}());

const withFunctionBasedListItemBehavior = (function () {
  function item(idx) {
    return this[idx];
  }
  return function() {
    this.item = item;
  };
}());


class ListWrapper {
  constructor(list) {

    // mixin in / explicit delegation
    withFunctionBasedListFirstBehavior.call(list);
    withFunctionBasedListLastBehavior.call(list);
    withFunctionBasedListItemBehavior.call(list);

    // forwarding
    this.first = function() {
      return list.first();
    };
    this.last = function() {
      return list.last();
    };
    this.item = function(idx) {
      return list.item(idx);
    }
  }
}


class Queue extends ListWrapper { // inheritance part I.
  constructor() {
    const list = [];

    super(list);                  // inheritance part II.

    // queue specific behavior
    this.enqueue = function(value) {
      list.push(value);
      return value;
    };
    this.dequeue = function() {
      return list.shift();
    };
  }
}


var queue = new Queue;

console.log("queue.enqueue('the') : ", queue.enqueue('the'));
console.log("queue.enqueue('quick') : ", queue.enqueue('quick'));
console.log("queue.enqueue('brown') : ", queue.enqueue('brown'));
console.log("queue.enqueue('fox') : ", queue.enqueue('fox'));

console.log("queue.first() : ", queue.first());
console.log("queue.last() : ", queue.last());
console.log("queue.item(2) : ", queue.item(2));

console.log("queue.dequeue() : ", queue.dequeue());
console.log("queue.dequeue() : ", queue.dequeue());
console.log("queue.dequeue() : ", queue.dequeue());

console.log("queue.first() : ", queue.first());
console.log("queue.last() : ", queue.last());
console.log("queue.item(2) : ", queue.item(2));

console.log("queue.dequeue() : ", queue.dequeue());
console.log("queue.dequeue() : ", queue.dequeue());

console.log('(queue instanceof Queue) : ', (queue instanceof Queue));
console.log('(queue instanceof ListWrapper) : ', (queue instanceof ListWrapper));
.as-console-wrapper { max-height: 100%!important; top: 0; }


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接