如何在JavaScript中使用mixin

mutmk8jj  于 2023-06-04  发布在  Java
关注(0)|答案(2)|浏览(285)

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

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

和扩展Computer类的Desktop类。我正在尝试将mixin功能挂接到Computer Class,如下所示:

computerQualityMixin.call(Computer.prototype);

并将其用于Desktop类的对象。下面是我的mixin代码;

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'

有人能帮我理解mixin吗?谢谢

thigvfpy

thigvfpy1#

这些评论提出了一个很好的问题,即您是否真的希望在这里使用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示例上调用这些方法。

ulydmbyx

ulydmbyx2#

Mixin应该被看作是一种方便的代码重用形式。
描述对象的某些行为并且也倾向于被一遍又一遍地复制的代码可以被认为是被收集/存储到mixin中。
使用JavaScript中的***基于函数的mixin-pattern***,人们还可以使用它的***有状态变体***,它为如何构建类型/对象架构提供了更多的可能性。
正如已经指出的那样,OP的示例在适合基于mixin的组合方面并不是很好。
然而,下一个给定的代码块确实尝试了一个稍微改变的示例,以演示在JavaScript中应用基于函数(有状态)的混合的不同方法......

// - the foolowing functions do implement each
//   a function-based "stateful mixin" which
//   preserves injected local state by creating
//   a closure at call/apply time.

// - targeting the introspection of local state.
function withLocalStateIntrospection(state) {
  function valueOf () {
    return { ...state };
  };
  function toString () {
    return JSON.stringify(state);
  };
  Object.defineProperties(this, {
    valueOf: { value: valueOf },
    toString: { value: toString },
  });
}

// - targeting hardware specific state getters.
function withHardwareStandardGetters(state) {
  Object.defineProperties(this, {
    manufacturer: { enumerable: true, get: () => state.manufacturer },
    processorSpeed: { enumerable: true, get: () => state.processorSpeed },
    ram: { enumerable: true, get: () => state.ram },
    hardDiskSpace: { enumerable: true, get: () => state.hardDiskSpace },
  });
}

// - targeting desktop specific state getters.
function withDesktopSpecificGetters(state) {
  Object.defineProperties(this, {
    bodyWidth: { enumerable: true, get: () => state.bodyWidth },
    bodyHeight: { enumerable: true, get: () => state.bodyHeight },
    bodyLength: { enumerable: true, get: () => state.bodyLength },
  });
}

// - the foolowing functions do implement each a
//   simple (no state handling) function-based mixin.

// - targeting hardware specific quality parameters.
function withHardwareSpecificQuality() {
  function getQuality () {
    return this.processorSpeed * this.ram * this.hardDiskSpace;
  };
  function isFast () {
    return this.processorSpeed > (this.ram / 4);
  };
  function isRoomy () {
    return this.hardDiskSpace > Math.floor(this.ram * this.processorSpeed);
  };
  Object.defineProperties(this, {
    getQuality: { value: getQuality },
    isFast: { value: isFast },
    isRoomy: { value: isRoomy },
  });
}

// - targeting desktop specific measures.
function withDesktopSpecificMeasures() {
  function getBodyVolume() {
    return this.bodyLength * this.bodyWidth * this.bodyHeight;
  };
  Object.defineProperty(this, 'getBodyVolume', { value: getBodyVolume });
}

// - examples of
//
//    - function based object composition
//       - at instance/object level,
//       - at class level.
//
//    - sub-classing

// - base/super class
class Computer {
  constructor(state) {
    // - applying 2 "stateful mixin"
    //   at instance/object level.
    withLocalStateIntrospection.call(this, state);
    withHardwareStandardGetters.call(this, state);
  }
}
// - making use of inheritance via the constructor's
//   prototype, but augmenting the latter by a more
//   generic mixin at class level.
withHardwareSpecificQuality.call(Computer.prototype);   

// - derieved class / sub-classing
class Desktop extends Computer {
  constructor(state) {

    super(state);

    // - applying a "stateful mixin"
    //   at instance/object level.
    withDesktopSpecificGetters.call(this, state);
  }
}
// - making use of inheritance via the constructor's
//   prototype, but augmenting the latter by a more
//   generic mixin at class level.
withDesktopSpecificMeasures.call(Desktop.prototype);

const desktop = new Desktop({

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

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

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

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.isFast() :", desktop.isFast());
console.log("desktop.isRoomy() :", desktop.isRoomy());
console.log("desktop.getQuality() :", desktop.getQuality());
console.log("desktop.getBodyVolume() :", desktop.getBodyVolume());

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

console.log(
  "\nObject.getOwnPropertyDescriptors(Desktop.prototype)) :",
  Object.getOwnPropertyDescriptors(Desktop.prototype)
);
console.log(
  "\nObject.getOwnPropertyDescriptors(Computer.prototype)) :",
  Object.getOwnPropertyDescriptors(Computer.prototype)
);
.as-console-wrapper { min-height: 100%!important; top: 0; }

你也可以考虑看一个更好的JavaScript示例,它也试图演示何时通过类扩展使用继承,何时不使用继承,何时只使用基于mixin/trait的组合,以及何时使用两者。
旁注-JavaScript中基于函数的Mixins / Traits / Talents的推荐资源

  • A fresh look at JavaScript Mixins由安格斯Croll从2011年5月
  • JavaScript的许多天赋,用于概括面向角色的编程方法,如2014年4月的Traits和Mixins。

此外,我建议阅读我在SO上列出的一些答案,这些答案也与这个主题有关。

  • ES 6类-混合
  • 如何在JavaScript中正确使用mixin
  • JavaScript中的特性

相关问题