-1

Hi everyone I have abstract class Computer:

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);
    }
}
And Desktop class extending the Computer Class. I'm trying to hook mixin functionality to Computer Class, like this:

computerQualityMixin.call(Computer.prototype);

and use it with objects that are of class Desktop. Here is my mixin code;

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);
    };
}
Problem is that I get 'undefined' of all the propertires I try to get:'this.ram' for example in my mixin, when I call some function:

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

Can someone help me with understanding the mixins? Thanks.

Ninjakannon
  • 3,751
  • 7
  • 53
  • 76
V.Dimitrov
  • 99
  • 1
  • 5
  • 3
    I guess you should read on how to use Stack Overflow snippets before anything else... – Angel Politis Nov 06 '16 at 20:55
  • How do you define `Desktop`? Are `ram`, `processorSpeed` and `hardDiskSpace` defined on `Computer.prototype`? If not, why do you call `computerQualityMixin` passing `Computer.prototype` as the `this` value? – Oriol Nov 06 '16 at 20:58
  • 1
    `Computer.prototype` is empty object. If the code is supposed to work as expected, `computerQualityMixin.call(this)` should be called at the end of constructor. The whole 'mixin' thing looks quite confusing. Since `Computer` has no parent, I see no reason why it can't have `getQuality`, etc methods defined in itself or parent class. – Estus Flask Nov 06 '16 at 21:08
  • Mixins come into play when you have two distinct classes you want to inherit from, but in your case the quality methods relate directly to the Computer class, so there is no real case for mixins here. – trincot Nov 06 '16 at 21:15

2 Answers2

0

The comments raise good questions about whether you really do want to use mixins here at all. But if you do, you might want to look at the articles by Angus Croll and Reg Braithwaite

Using the techniques from the former, you could rewrite as

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

Then you should be able to call those methods on you Computer instances.

Scott Sauyet
  • 49,207
  • 4
  • 49
  • 103
0

Mixins should be seen as a handy form of code reuse.

Code that describes certain behavior of an object and also tends to be copied over and over again, might be considered of being collected/stored once into a mixin.

With the function-based mixin-pattern in JavaScript one also can make use of its stateful variant that offers even more possibilities of how one might going to built ones type/object architecture(s).

As already has been pointed, the OP's example is not that well chosen in terms of fitting to mixin-based composition.

The next given code block nevertheless does try with a slightly changed example in order to demonstrate the different approaches of applying function-based (stateful) mixins in 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; }

One might consider having a look at a much better JavaScript example too, that also tries to demonstrate when to use inheritance via class extension and when not, when to use just mixin/trait-based composition and also when to use both.

side note - recommended resources on functions-based Mixins / Traits / Talents in JavaScript

Additionally I do recommend reading some of the listed answers of mine given on SO, that are related to this topic too.

Peter Seliger
  • 11,747
  • 3
  • 28
  • 37