views:

77

answers:

3

If I have an object that I want to "inherit" methods from a "super object" to ensure consitency. They will be intermingling variables. REVISED

ParentObj = function()
{
    var self = this;
    this.interval = null
    this.name = "";
    this.timeout = 1000;

    this.stop = function()
    {
        clearInterval(self.interval);
    };

    this.start = function()
    {
        self.log("Starting up");
        setTimeout(function(){
            self.getData(true);
            self.interval = setInterval(function(){
                self.getData();
            }, self.timeout)
        }, 1000);
    };

    this.log = function(msg)
    {
        require("sys").log(self.name + ": " + msg);
    };

    this.getData = function(override)
    {
        if(override || self.interval)
        {
            /**
            * Allow
            */
            self.log(new Date());
        }
        else
        {
            self.log("Unable to override and no interval");
        }
    }
}

ChildObj = function()
{
    var self = this;
    this.name = "Child";
    this.timeout = 500;
    this.start();
    setTimeout(function(){
        self.stop();
    }, 2000);
}

ChildObj.prototype = new ParentObj();


var c = new ChildObj();

This doesn't seem to work correctly, specifically it not seeing the self.interval and is unable to clear it.

I'm open to other JavaScript inheritance methods, if they exist, but I really need to start encapsulating stuff off into the parent. There are three or four functions that are identical, but being changed at times and this means I have to run through a dozen files to make the change, rather then simply altering the parent.

Working through some of the suggestions I've tried to more clearly define what sort of functionality I'm going for. Ideally all the "children" will have a couple of unique settings (name, interval, config settings) and a getData() method while the parent manages starting, stopping, logging, and anything else.

A: 

First thing, I don't think that var this.interval is okay. var keyword is used to define a variable, but in your case you are referencing an object.

Second, if you want to declare "interval" as a method of your cobj, you have to wrap the body of the function in a function.

So, this way it works for me:

var sobj = function()
{
    this.close = function()
    {
        clearInterval(this.interval);
    }
}
 var cobj = function()
{   
   this.initInterval = function(){ this.intervalid = setInterval(function(){ alert("test")}, 5000)};
   this.intervalid = null;
}
cobj.prototype = new sobj();

var inst = new cobj();
inst.initInterval();

After I've defined the constructor functions, I create an actual instance of a "cobj" object and then call "initInterval" to initialize the "setInterval".

UPD: updated the code per @MooGoo's comment.

naivists
Whoever gave the downvote, please comment - I'd really like to know where I am wrong.
naivists
The -1 wasn't from me, but this is very wrong. You do not need `var self = this`. In this case it is the same to just do `this.interval =`. Also you are assigning a function to `interval`, not the interval id. The correct code would be `this.interval = setInterval(/*func*/, 1000);`
MooGoo
@MooGoo, thanks for the comment, I somehow didn't catch the idea that OP wanted to initialize the interval in the constructor. Updated my code.
naivists
var self = this is a useful bit of syntactic sugar that helps when you start using inner functions. For this particular example it is not necessary, but is a helpful practice if you go back and make changes. Where you are wrong is the need for a wrapper around the setInterval. It's just not necessary, setInterval will be called when the object is instantiated.
C-Mo
A: 

Using this inheritance method, you can only mingle variables "upstream." Your child object will be able to see the public properties of its prototype, but the prototype cannot see the properties of its children. It must be self-contained.

(EDIT: I just noticed you're also using "self" without declaring it in sobj.)

sobj = function()
{
    var self = this;
    self.close = function()
    {
        clearInterval(self.interval);
    }

    self.interval = null;
}
cobj = function()
{
    var self = this;
    self.interval = setInterval(function(){ /* Do something */}, 1000);
}
// set cobj.prototype - see explanation below

For how to properly set the prototype (and an in-depth look at how inheritance works in JS), I refer you to Douglas Crockford's book JavaScript: The Good Parts.

He has actually posted how to properly set the prototype on his site. Be sure to use the version that does not touch Object.prototype, as many scripts (jQuery for starters) will break if you change it.

C-Mo
Don't call the constructor on the super to get an object instance as the prototype for the sub, it can have nasty side effects and break things. Instead, 'clone' the super's prototype and attach that to the sub... see my answer.
no
Oops, was a little too quick on the copy and paste. +1 for catching my mistake, I'll fix it.
C-Mo
@C-Mo: Is there another inheritance method I can use? One that perhaps doesn't do this upstream mergeing?
Josh K
You would need to use a fair bit of syntactic sugar (no's answer is a good example - Crockford also has some methods for emulating classical inheritance on his site). But in terms of "sugarless" JavaScript, no. If you say `foo.x = 5` and x is not explicitly defined as a member of foo, JavaScript looks at `foo.prototype` and continues searching all the way back to `Object.prototype`. It doesn't look the other way. Because JavaScript is duck typed, extending a specific interface isn't as important as in languages like C++ or Java.
C-Mo
+1  A: 
  • 'Clone' an object by making the object the prototype of a throwaway function and calling that function with 'new'.

  • Clone the parent constructor's prototype, and set the result as the prototype of the child class.

...

/**
 * Extend a constructor with a subtype
 * @param {Function} superCtor      Constructor of supertype
 * @param {Function} subCtor        Constructor of subtype
 * @return {Function}               Constructor of subtype
 */
var extend = (function(){

  return function (superCtor, subCtor) {
    var oldProto=subCtor.prototype;
    subCtor.prototype=clone(superCtor.prototype);
    return merge(subCtor.prototype, oldProto).constructor=subCtor; 
  }

  function Clone(){}

  /**
   * Clone an object
   * @param {Object}  obj     Object to clone
   * @return {Object}         Cloned object
   */
  function clone (obj) { Clone.prototype=obj; return new Clone() }

  /**
   * Merge two objects
   * @param {Object} dst      Destination object
   * @param {Object} src      Source object
   * @return {Object}         Destination object
   */
  function merge (dst, src) {
    for (var p in src) if (src.hasOwnProperty(p)) dst[p]=src[p];
    return dst;
  }

}());
no