【问题标题】:How to use mixin in Javascript如何在 JavaScript 中使用 mixin
【发布时间】:2017-03-20 03:52:50
【问题描述】:

大家好,我有抽象类计算机:

class Computer {
    constructor(manufacturer, processorSpeed, ram, hardDiskSpace) {
        if (new.target === Computer) {
            throw new Error("Cannot instantiate directly.");
        }
        this.manufacturer = manufacturer;
        this.processorSpeed = Number(processorSpeed);
        this.ram = Number(ram);
        this.hardDiskSpace = Number(hardDiskSpace);
    }
}
和桌面类扩展了计算机类。 我正在尝试将 mixin 功能与计算机类挂钩,如下所示:

computerQualityMixin.call(Computer.prototype);

并将它与桌面类的对象一起使用。这是我的混合代码;

function computerQualityMixin() {
    let ram = this.ram;
    let processorSpeed = this.processorSpeed;
    let hardDiskSpace = this.hardDiskSpace;
    this.getQuality = () => {
        return processorSpeed
            * ram
            * hardDiskSpace;
    };
    this.isFast = () => {
        return processorSpeed > ram / 4;
    };
    this.isRoomy = () => {
        return hardDiskSpace > Math.floor(ram * processorSpeed);
    };
}
问题是,当我调用某个函数时,我尝试获取的所有属性都“未定义”:“this.ram”例如在我的 mixin 中:

let desktop = new Desktop("JAR Computers", 3.3, 8, 1);
console.log(desktop.getQuality());//Returns NaN because try to make Math operations with 'undefined'

有人可以帮助我理解 mixins 吗?谢谢。

【问题讨论】:

  • 我想你应该先阅读如何使用 Stack Overflow sn-ps...
  • 你如何定义DesktopramprocessorSpeedhardDiskSpace 是否在 Computer.prototype 上定义?如果不是,你为什么调用computerQualityMixin 传递Computer.prototype 作为this 值?
  • Computer.prototype 是空对象。如果代码应该按预期工作,computerQualityMixin.call(this) 应该在构造函数的末尾调用。整个“混合”的事情看起来很混乱。由于Computer 没有父类,我看不出它为什么不能在自身或父类中定义getQuality 等方法。
  • 当你有两个不同的类想要继承时,Mixins 就会发挥作用,但在你的情况下,质量方法直接与 Computer 类相关,因此这里没有真正的 mixins 案例。跨度>

标签: javascript oop ecmascript-6 mixins composition


【解决方案1】:

Mixins 应该被视为一种方便的代码重用形式。

描述对象的某些行为的代码,以及 倾向于被一遍又一遍地复制,可以考虑 被收集/存储一次到一个mixin中。

在 JavaScript 中使用基于函数的 mixin/trait 模式 还可以利用其提供的有状态变体 关于如何安排的更多可能性 类型/对象架构。

正如已经指出的那样,OP 的示例并不是那么好 选择适合基于 mixin/trait 的组合。

下一个给定的代码块仍然尝试稍微 更改变体以展示不同的方法 在 JavaScript 中应用基于函数的 mixin/trait 模式 ...

function withObjectBaseIntrospection(state) { // - mixin that preserves injected
    var                                       //   local state by creating a
        object = this;                        //   closure at call/apply time.

    object.valueOf = function () {
        return Object.assign({}, state);
    };
    object.toString = function () {
        return JSON.stringify(state);
    };
}


function withHardwareStandardGetters(state) { // - mixin that preserves injected
    var                                       //   local state by creating a
        hardware = this;                      //   closure at call/apply time.

    Object.defineProperty(hardware, "manufacturer", {
        get: function () { return state.manufacturer; }
    });
    Object.defineProperty(hardware, "processorSpeed", {
        get: function () { return state.processorSpeed; }
    });
    Object.defineProperty(hardware, "ram", {
        get: function () { return state.ram; }
    });
    Object.defineProperty(hardware, "hardDiskSpace", {
        get: function () { return state.hardDiskSpace; }
    });
}
function withDesktopSpecificGetters(state) {  // - mixin that preserves injected
    var                                       //   local state by creating a
        hardware = this;                      //   closure at call/apply time.

    Object.defineProperty(hardware, "bodyLength", {
        get: function () { return state.bodyLength; }
    });
    Object.defineProperty(hardware, "bodyWidth", {
        get: function () { return state.bodyWidth; }
    });
    Object.defineProperty(hardware, "bodyHeight", {
        get: function () { return state.bodyHeight; }
    });
}


function withHardwareSpecificQuality() {  // - generic function based mixin pattern.
    this.getQuality = function() {
        return (this.processorSpeed * this.ram * this.hardDiskSpace);
    };
    this.isFast = function () {
        return (this.processorSpeed > (this.ram / 4));
    };
    this.isRoomy = function () {
        return (this.hardDiskSpace > Math.floor(this.ram * this.processorSpeed));
    };
}
function withDesktopSpecificMeasures() {  // - generic function based mixin pattern.
    this.getBodyVolume = function() {
        return (this.bodyLength * this.bodyWidth * this.bodyHeight);
    };
}


class Computer {
    constructor(state) {

        withObjectBaseIntrospection.call(this, state);  // - applying 2 "stateful mixin"
        withHardwareStandardGetters.call(this, state);  //   at instance/object level.
    }
}
withHardwareSpecificQuality.call(Computer.prototype);   // - making use of inheritance via the
                                                        //   constructor's prototype, but enriching the
                                                        //   latter by a more generic mixin (at "class level").
class Desktop extends Computer {  // - newly available
    constructor(state) {          //   syntactic sugar for the more
                                  //   "class like" inheritance pattern.
        super(state);
        withDesktopSpecificGetters.call(this, state);   // - applying a "stateful mixin"
    }                                                   //   at instance/object level.
}
withDesktopSpecificMeasures.call(Desktop.prototype);    // - making use of inheritance via the
                                                        //   constructor's prototype, but enriching the
                                                        //   latter by a more generic mixin (at "class level").
let
    desktop = new Desktop({

        manufacturer: "JAR Computers",
        processorSpeed: 3.3,
        ram: 8,
        hardDiskSpace: 1,

        bodyWidth: 300,
        bodyHeight: 40,
        bodyLength: 300
    });

console.log("Desktop.prototype : ", Desktop.prototype);
console.log("Computer.prototype : ", Computer.prototype);

console.log("(desktop instanceof Desktop) ? ", (desktop instanceof Desktop));
console.log("(desktop instanceof Computer) ? ", (desktop instanceof Computer));

console.log("desktop.manufacturer : ", desktop.manufacturer);
console.log("desktop.processorSpeed : ", desktop.processorSpeed);
console.log("desktop.ram : ", desktop.ram);
console.log("desktop.hardDiskSpace : ", desktop.hardDiskSpace);

console.log("desktop.getQuality() : ", desktop.getQuality());
console.log("desktop.getBodyVolume() : ", desktop.getBodyVolume());

console.log("desktop.valueOf() : ", desktop.valueOf());
console.log("desktop.toString() : ", desktop.toString());

人们也可以考虑看看一个更好的 JavaScript 示例,它也试图演示 when to use inheritance via class extension and when not, when to use just mixin/trait based composition and also when to use both

旁注 - 基于 JavaScript 中的 Mixins / Traits / Talents 函数的推荐资源

此外,我确实建议阅读我在 SO 上给出的一些列出的答案,这些答案也与该主题相关。

【讨论】:

    【解决方案2】:

    cmets 提出了一个很好的问题,即您是否真的想在这里使用 mixin。但是如果你这样做,你可能想看看Angus CrollReg Braithwaite的文章

    使用前者的技术,你可以重写为

    const asComputerQuality = function() {
      this.getQuality = function() {
        return this.processorSpeed
               * this.ram
               * this.hardDiskSpace;
      };
      this.isFast = function() {
        return this.processorSpeed > this.ram / 4;
      };
      this.isRoomy = function() {
        return this.hardDiskSpace > Math.floor(this.ram * this.processorSpeed);
      };
    }
    
    asComputerQuality.call(Computer.prototype);
    

    那么您应该可以在 Computer 实例上调用这些方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-17
      • 2015-03-09
      • 2019-02-16
      • 2018-12-26
      相关资源
      最近更新 更多