在Javascript中Mixins和继承之间有什么实际区别? [英] What are the practical differences between Mixins and Inheritance in Javascript?

查看:68
本文介绍了在Javascript中Mixins和继承之间有什么实际区别?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

只是(模拟)多重继承是混合的唯一优势:

Is just (simulating) multiple inheritance the only advantage of mixing:

 Object.assign( MyClassA.prototype, MyMixinB )

与继承

 class MyClass extends MyClassB {

 // MyClassB = class version of MyMixinB


$ b ES6 Javascript中的$ b

in ES6 Javascript?

谢谢

推荐答案

没有一个简短的答案。 JavaScript(JS)为构建类型/对象系统提供了许多不同的技术方法(以及如何组合它们)的实用性。

There will be not that one short answer. The practicality comes with the many different technical approaches (and how one combines them) that JavaScript (JS) does offer for building type/object systems.

首先,作为OP (原始海报)已经意识到,JS中没有真正的/真正的多重继承。无论是选择类还是传统的基于纯函数的样式,这种语言都只提供基于原型的单一继承(可以链接)。

First, as the OP (original poster) already is aware of, there is no real/true multiple inheritance in JS. This language just offers prototype based single inheritance (that can be chained) regardless if one chooses classes or the traditional pure function based style.

但是,其次,与其他编程一样语言(PL),还有另一种不基于继承的代码重用和对象组合方式......它是基于mixin和/或trait的组合。

But, secondly, as in other programming languages (PL) too, there is another not inheritance based way of code reuse and object composition ... it's mixin and/or trait based composition.

但我会坚持通过OP提供的示例代码。

But I will stick to both of the by the OP provided example codes.

Object.assign(MyClassA.prototype, MyMixinB)

如果有人认为 MyMixinB 提供,这已经很好了作为基于对象的mixin 的附加行为,例如...

This already is a nice one, if one thinks of MyMixinB providing additional behavior as object based mixin like e.g. ...

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 list-behavior可以分配给任何类似列表的结构。但是第一个示例的OP确实将mixin分配给原型。因此,除非没有自己的 last 方法的实例将调用它,否则不会使用这种新获得的行为。然后进行原型委派...现在原型 last 将在调用对象的上下文中调用。

The above approach is a combination of composition and inheritance. The withObjectBasedListLastBehavior mixin provides a generically implemented last method for list like structures. This structure by itself does not effect anything. Via Object.assign the last list-behavior can be assigned to any list-like structure. But the OP with the first example does assign a mixin to a prototype. Thus there is no usage of such newly gained behavior unless an instance that does not feature an own last method is going to invoke it. Then prototypal delegation takes place ... the now prototypal last will be invoked within the context of the calling object.

通过mixins(具有这种行为的结构)提供额外行为/功能的优势在于可以灵活地重复使用这种结构。理想的mixin是原子的,只有一个特定的行为(一种方法)。这种行为可以从类构造函数/构造函数的主体中混合,或者与上面的示例一样,它被分配给任何对象(原型对象或任何其他类型)。因此,通过mixins / traits在JavaScript中进行代码重用可以在2级...类级别(构造/实例化时)和对象级别的任何时间。

The advantage of providing additional behavior/functionality via mixins (structures that carry such behavior) comes with the flexibility of how this structures can be repeatedly used. The ideal mixin is atomic and features just one specific behavior (one method). This behavior than can be mixed from within the bodies of class constructors / constructor functions or, as with the above example, it gets assigned to any object (prototype objects or any other type). Thus code reuse in JavaScript via mixins/traits is possible at 2 levels ... at class level (at construction/instantiation time) and at any time at object level.

这种灵活性应该不会令人感到意外,因为mixins / traits只是从行为的角度来看构成(一个对象 具有行为),而真正的继承,也在JS中,是负责 事物的关系。

This flexibility should be not that surprising since mixins/traits contribute to composition just the behavior point of view (an object has a behavior) whereas real inheritance, also in JS, is responsible for the is a thing relationship.

应该知道mixin / trait基于组合之间的差异和继承不是JavaScript特有的东西。这些概念也适用于其他PL。技术上JS更灵活,因为它是基于对象的,并且具有两种授权方式,隐式通过自动运行的原型委派和显式直接通过调用传递上下文致电申请功能方法。

One should be aware that the differences between mixin/trait based composition and inheritance is not a JavaScript specific thing. The concepts apply to other PLs too. Technically JS is just more flexible, for it is object based and does feature two ways of delegation, implicit via the automatically running prototypal delegation and explicit with passing context directly via invoking the call and apply methods of functions.

实用证明以上提供的强烈意见考虑了OP的第二个示例代码......

A practical proof of the above provided strong opinion takes into account the OP's second example code ...

class MyClass extends MyClassB {

// MyClassB = class version of MyMixinB

...并确实将其更改为类似的东西...

... and does change it to something like that ...

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; }

这篇关于在Javascript中Mixins和继承之间有什么实际区别?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆