如何在 Javascript 中使用 mixin

How to use mixin in Javascript

大家好我有摘要class电脑:

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);
    }
}
和桌面 class 扩展计算机 Class。 我正在尝试将 mixin 功能连接到计算机 Class,如下所示:

computerQualityMixin.call(Computer.prototype);

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

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);
    };
}
问题是我得到了我尝试获得的所有属性的 'undefined':'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 吗?谢谢。

这些评论提出了很好的问题,即您是否真的想在这里使用 mixins。但如果你这样做,你可能想看看 Angus Croll and Reg 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 个实例上调用这些方法。

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

描述对象特定行为的代码,以及 往往被一遍又一遍地复制,可能会被考虑 曾经 collected/stored 成为混音。

使用 JavaScript 中基于 mixin/trait 模式的函数 还可以利用其提供的有状态变体 人们可能会如何安排的更多可能性 type/object 个架构。

正如已经指出的那样,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/Taltens 的函数的推荐资源

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

  • ES 6 Classes - Mixins
  • How to use mixins properly in Javascript
  • Traits in javascript