0

What I want to do is retrofit a serialisation method to an existing JavaScript type, which is a member of an inheritance tree. It is very nearly working, but my function is operating on the parent type rather than the type I am trying to associate it with. So to take a very basic example:

function Animal( species, population ) 
{
   this.species = species;
   this.population = population;
}

Animal.prototype.getDetail = function() 
{
   return "Species: "+this.species+" Population: "+this.population;
}

function Mammal( species, population, bodyTemperature )
{
    Animal.apply(this, [species, population]);
    this.bodyTemperature = bodyTemperature;
}
Mammal.prototype= new Animal();
Mammal.constructor = Mammal;

var Serialise= {
    init: function() {
        Mammal.prototype.serialise = this.serialiseMammal.bind(Mammal.prototype);
    },

    serialiseMammal: function() {
       return {
            species: this.species,
            population: this.population,
            bodyTemperature: this.bodyTemperature
       };
    }   
}

Serialise.init();

var bob = new Mammal("Human", 7000000000, 36.6);

console.log( bob.serialise() );

What is happening is that the serialise function is running in the context of the Animal type which is correct because that is the prototype of Mammal, but if I try to bind it directly to the Mammal, nothing happens at all. Once again, I find myself out of my depth in JavaScript's scope.

The reason I am doing this is to keep all my serialisation/deserialisation in one place, so that it's there if I need it, but I don't always have to have it included- I guess in another language it might be behaving slightly like a mixin.

What do I need to do to ensure that the any call of serialise() on an instance of the Mammal type will correctly apply the Serialise.serialiseMammal()function in the context of the current instance? Is there a way of doing this without binding the functions to individual instances when they are created?

I'm also trying to improve my own expertise with the language and doing things in more of a JavaScript way, so if the whole approach is totally wack or against the grain of the language, that would be good to know.

Alex
  • 21,273
  • 10
  • 61
  • 73
glenatron
  • 11,018
  • 13
  • 64
  • 112
  • Why are you doing it this way, and not just putting the function `serialise` directly on the `Mammal` prototype? Also, why do you have `Mammal.constructor = Mammal`? – deitch Dec 16 '14 at 12:14
  • Ideally I want to keep deserialisation and serialisation together in one place and I don't need them most of the time, only in a couple of scenarios. I have a lot of types that need to be serialised and deserialised in a nested fashion and having to spend the whole time dashing around my code like that was a pain. The constructor is explained here: http://stackoverflow.com/questions/8453887/why-is-it-necessary-to-set-the-prototype-constructor - possibly not necessary in my example, but I am using it in the real code. – glenatron Dec 16 '14 at 12:22
  • On the constructor: OK, I see what you are doing. Not necessary in this case, but doesn't hurt. But you need to do `Mammal.prototype.constructor` like in the example, and not `Mammal.constructor`. – deitch Dec 16 '14 at 12:25
  • On the serialization, are you saying that you have lots of different types that need serialization/deserialization functions, and so instead of having to put it on all of the prototypes, you just want to decorate them? If so, aren't you already putting in place special Mammal-specific code with `serialiseMammal()`? – deitch Dec 16 '14 at 12:26
  • Whatever the answer is on why you are doing it, the scope issue is below. – deitch Dec 16 '14 at 12:33
  • I'm considering TypeScript and looking at EC6/7 because this stuff does my head in. – Peter Wone May 12 '15 at 21:45

1 Answers1

1

I do not fully understand why you want to decorate it this way, as opposed to just putting the specialized serialiseMammal directly on the Mammal.prototype, as below:

Mammal.prototype.s2 = function() {
    return {
        species: this.species,
        population: this.population,
        bodyTemperature: this.bodyTemperature
    };
};

I called this s2 so as not to conflict. This is the identical code from serialiseMammal. However, if you prefer the decorator-style pattern you have used, then you just need to do:

var Serialise= {
    init: function() {
        Mammal.prototype.serialise = this.serialiseMammal;
    },

    serialiseMammal: function() {
        return {
            species: this.species,
            population: this.population,
            bodyTemperature: this.bodyTemperature
       };
    }   
}

Within the context of init, then this refers to Serialise, so this.serialiseMammal is the function you have. But it is just a function, nothing more. When you next call bob.serialise(), the function is called in the context of the moment. This, this inside of bob.serialise(), which is the code from serialiseMammal, is set to bob.

Here is a fiddle with it. http://jsfiddle.net/6un6qho1/1/

deitch
  • 14,019
  • 14
  • 68
  • 96
  • Great, thanks! JavaScript has trained me to ignore the obvious answer because it basically never works. Should have tried it this time :) – glenatron Dec 16 '14 at 14:13
  • LOL! Truth is, I have learned to really like JS (the DOM in the browser and its attendant methods are a whole other can of worms). – deitch Dec 16 '14 at 16:29