0

I'm looking for a clean and simple way to mixin methods to different classes. Most of the examples I've been able to find use the JS prototype, like this

Example code:

const _ = require("underscore")

let actions = {
  speak() {
    console.log(this.name + " animal speak")
  },

  look() {
    console.log(this.name + " animal looks")
  }
}

class Dog {
  constructor(name) {
    console.log("new Dog", name)
    this.name = name

    // modify instance and return
    let that = _.extend(this, actions)
    return that
  }

  speak() {
    console.log(this.name + " dog speaks")
  }

  bark() {
    console.log(this.name + " dog barks")
  }

}

function test() {
  let rover = new Dog("rover")

  // speak in "actions" overrides Dog.speak method
  rover.speak() // => rover animal speak

  // runtime works but ts-lint doesn't like it
  // look property doesn't exist on type 'dog'
  rover.look() // => rover animal looks

  // from dog since the method doesn't exist on actions
  rover.bark() // => rover dog barks
}

test()

So to use the prototype I could modify the above as:

Object.assign(Dog.prototype, actions)

and then just use a vanilla constructor that returns the this

class Dog {

  constructor(name) {
    this.name = name
  }
...
}

Object.assign(Dog.prototype, actions)

In both cases the mixin speak method will replace the Dog Class speak method, ok.

So my question is: if there is any other simpler/cleaner method to mixin methods across Classes?

And is there anything wrong with the above in terms of adding to a prototype? Is that creating copies of the actual methods? If it's only in the prototype and not every instance, I guess it's OK, but not entirely clear of any memory implications there.

dcsan
  • 11,333
  • 15
  • 77
  • 118
  • https://stackoverflow.com/q/42247434 – Robert Harvey May 10 '19 at 18:43
  • @RobertHarvey thanks, actually similar. brings up the good point of calling mixed in methods from the main Class, which works as i have it. – dcsan May 10 '19 at 19:17
  • My method basically works, including getter/setters. What other edge cases are there in handling classes/instances where this technique might cause problems? – dcsan May 10 '19 at 19:30
  • one problem of the above is that TSLint is not aware of the methods being mixed in, so I get a lot of TS lint errors, cannot jump to implementation etc. – dcsan May 12 '19 at 06:44
  • memo - more examples https://alligator.io/js/class-composition/ – dcsan May 12 '19 at 06:47
  • The OP of this Q gave some other mixin examples. Some answers discuss the given approaches ... https://stackoverflow.com/questions/50614981/what-is-the-best-way-to-use-mixins-in-js – Peter Seliger May 16 '19 at 12:37

1 Answers1

0

As an alternative to using mixins you can use inheritance using extends to override methods on a class.

The disadvantage of inheritance compared to mixins is you can only extend one class at a time, but you can use multiple mixins. So it depends on what your use case is for which one you use.

Here is an example:

class Animal {
  constructor(name) {
    console.log("new", this.constructor.name, name)
    this.name = name

  }
  
  speak() {
    console.log(this.name + " animal speak")
  }

  look() {
    console.log(this.name + " animal looks")
  }
}

class Dog extends Animal {
  constructor(name) {
    super(name)
  }

  speak() {
    console.log(this.name + " dog speaks")
  }

  bark() {
    console.log(this.name + " dog barks")
  }

}

const dog = new Dog('Fred')
// Will call the overridden speak method on Dog
dog.speak() // Fred dog speaks
// Will call look on Animal as it hasn't been overriden in Dog
dog.look() // Fred animal looks
braza
  • 4,260
  • 1
  • 26
  • 36
  • 2
    Aren't mixins sort of supposed to be an **alternative** to inheritance? From [this page](https://en.wikipedia.org/wiki/Mixin): *"A mixin is a class that contains methods for use by other classes without having to be the parent class of those other classes.... Mixins are sometimes described as being "included" rather than "inherited".* – Robert Harvey May 10 '19 at 18:46
  • 1
    Yeah, but I think the main advantage of a mixin is to get around multiple inheritance. Maybe that is what @dcsan is trying to do, but in their example it seems that using class inheritance would work. – braza May 10 '19 at 18:51
  • Mixins are generally used for "multiple inheritance", you can only inherit a single prototype using the `extends` keyword. – Jake Holzinger May 10 '19 at 18:51
  • I've put this answer to suggest a potentially "simpler/cleaner" solution as requested, but it may not fit the requirements if multiple inheritance is needed – braza May 10 '19 at 18:52
  • Regardless, you don't appear to be answering the question asked. The OP asks for mixins, but you offer ordinary inheritance. – Robert Harvey May 10 '19 at 18:55
  • You're right. I'll probably leave it up as an alternative way of doing it, but for mixins, your link to the other question is probably the best answer. – braza May 10 '19 at 19:00
  • thanks but I'm definitely looking to composition not inheritance, I find that's a far too restrictive design cul-de-sac. Will clarify. – dcsan May 10 '19 at 19:12
  • Thanks for clarifying. Do you think I should remove my answer, or does it still provide some value? – braza May 10 '19 at 19:14
  • the inheritance method *does* provide a proper hierarchy of the method calls. The mixin method I have, I can never call `Dog.speak()` that method is overwritten by the actions method of the same name when I do the Object.assign. If I merge objects in the other direction, things don't work. – dcsan May 10 '19 at 19:35