-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,551
  • 6
  • 50
  • 70
V.Dimitrov
  • 89
  • 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
  • 44,568
  • 4
  • 43
  • 95
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/trait pattern in JavaScript one also can make use of its stateful variant that offers even more possibilities of how one might going to arrange 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/trait based composition.

The next given code block nevertheless does try with a slightly changed variant in order to demonstrate the different approaches of applying function based mixin/trait patterns in JavaScript ...

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

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.

Community
  • 1
  • 1
Peter Seliger
  • 8,830
  • 2
  • 27
  • 33