0

Well the title is a mouthful but I couldn't come up with a better one, ideas welcome.
Anyhow, I have a javascript object containing classes as properties. I want to create another object which is in every aspect equal to the first one by subclassing it. I'm gonna try to sum it up:

var L1 = {};

L1.Foo = function() {/*...*/};
L1.Bar = function() {/*...*/};
//...
L1.Baz = function() {/*...*/};

var L2 = {};

L2.Foo = function() { L1.Foo.call(this); /*possibily some other code here*/ };
L2.Foo.prototype = Object.create(L1.Foo.prototype);
L2.Foo.prototype.constructor = L2.Foo;

L2.Bar = function() { L1.Bar.call(this); /*possibily some other code here*/ };
L2.Bar.prototype = Object.create(L1.Bar.prototype);
L2.Bar.prototype.constructor = L2.Bar;

//...

L2.Baz = function() { L1.Baz.call(this); /*possibily some other code here*/ };
L2.Baz.prototype = Object.create(L1.Baz.prototype);
L2.Baz.prototype.constructor = L2.Baz;

var foo = new L2.Foo();
console.log(foo); //L2.Foo
var bar = new L2.Bar();
console.log(bar); //L2.Bar
var baz = new L2.Baz();
console.log(baz); //L2.Baz

First, working version.
I told myself: "huh, looks like there a pattern here" so I went and modified my code as follows:

//first 10 lines unaltered

for(prop in L1) {
    L2[prop] = function() { L1[prop].call(this); /*Call super method by default,
    unless overriden below*/ };
    L2[prop].prototype = Object.create(L1[prop].prototype);
    L2[prop].prototype.constructor = L2[prop];
}
//Here I decide that I want to override only the constructor
//for Foo, so naturally:
L2.Foo.prototype.constructor = function() {
    L1.Foo.call(this);
    this.someOtherProperty = "foo";
};

var foo = new L2.Foo();
console.log(foo); //L2.(anonymous function)?
console.log(foo.someOtherProperty); //undefined?
var bar = new L2.Bar();
console.log(bar); //L2.(anonymous function)?
var baz = new L2.Baz();
console.log(baz); //L2.(anonymous function)?

Second, not-so-working version.
What I am getting wrong?

Andrea Aloi
  • 911
  • 1
  • 13
  • 30

2 Answers2

1

"huh, looks like there a pattern here" so I went and modified my code as follows:

for(prop in L1) {
    L2[prop] = function() { L1[prop].call(this);

You've hit the common closure in a loop problem - all your L2 functions are actually calling L1.Baz on their new instance as prop will have the value "Baz". See the linked question for how to fix this.

Also, notice that none of your constructors does pass its arguments to the super call, which might bite you as well.


Here I decide that I want to override only the constructor for Foo, so naturally:

L2.Foo.prototype.constructor = function() {
    L1.Foo.call(this);
    this.someOtherProperty = "foo";
};

What I am getting wrong?

Overwriting the .constructor property on a prototype object does nothing. Your code is still invoking new L2.Foo, not new L2.Foo.prototype.constructor. You might want to have a look at how the new keyword works.

Instead, you really need to replace L2.Foo. This can be done with this pattern:

L2.Foo = (function (original) {
    function Foo() {
        original.apply(this, arguments);   // apply old L2.Foo constructor
        this.someOtherProperty = "foo";    // set property
    }
    Foo.prototype = original.prototype; // reset prototype
    Foo.prototype.constructor = Foo; // fix constructor property
    return Foo;
})(L2.Foo);

(or you just put your standard pattern from the first version). If this does get too repetitive, you might also do the .prototype and .constructor setup programmatically:

// whole code
var L2 = {
    Foo: function() {
        L1.Foo.call(this);
        this.someOtherProperty = "foo";
    }
    // … other overwritten constructors
};
for (var prop in L1) {
    if (!L2[prop]) // unless overridden above, create default that only…
        (function(parent) {
            L2[prop] = function() {
                parent.apply(this, arguments); // calls super
            };
        }(L1[prop]));
    L2[prop].prototype = Object.create(L1[prop].prototype);
    L2[prop].prototype.constructor = L2[prop];
}
Community
  • 1
  • 1
Bergi
  • 513,640
  • 108
  • 821
  • 1,164
  • Closure in a loop, of course! I feel so dumb, I should know better by now! Thanks for your (thoroughly documented) solution, I will give it a shot over lunch break and hit you back. I'm gonna go ahead and accept it anyway. – Andrea Aloi Oct 07 '14 at 07:47
  • You hit the common "closure in loop" pitfall as well, only noticed now: in anonymous `function(parent) {...}` you still referenced `prop`. I fixed it [here](http://jsfiddle.net/jkqayayq/3/) but the problem of `L2.Bar` and `L2.Baz` functions being reported as "anonymous functions" still persists :( Haven't gone through the links you provided though, they might shine some light. For the time being, thanks. – Andrea Aloi Oct 07 '14 at 11:22
  • Also, I wasn't allowed to edit your post because edits have to be at least 6 characters long, but I believe the brackets around the anonymous function in the `for` loop could should be changed from this form: `(function(parent) {..}(L1[prop]))` to this: `(function(parent) {..})(L1[prop])`. – Andrea Aloi Oct 07 '14 at 11:31
  • I'm only referencing `prop` to assign the new constructor to its place, I'm not using it *inside* the constructor. That you are getting `L2.(anonymous functions)` is not a problem, it is only natural - those functions *are* not named. If you wanted such, have a look [here](http://stackoverflow.com/q/9479046/1048572). And no, the [parenthesis are correct](http://stackoverflow.com/q/3384504/1048572) – Bergi Oct 07 '14 at 11:31
  • Indeed, I won't sweat it, and yes you're perfectly right about the closure issue, I had put a `console.log(prop)` *inside* the constructor and it was always logging `Baz`; just outside it, though, it logs `Bar` then `Baz`. Cheers. – Andrea Aloi Oct 07 '14 at 11:36
  • About parenthesis: my bad, again, didn't realize how noob I was till now :) Will read the link you posted to address the anonymous function issue, though I'm thinking of just living with it. – Andrea Aloi Oct 07 '14 at 12:16
0

This might just be me, but if the subclass needs to be the same in every aspect as the superclass, why bother making a subclass? I'm not entirely clear an what you are trying to achieve from the code.

But just give the class a property of the class itself e.g. (in plain java)

public class TestInheritClass {

   private TestInheritClass tic;

   public TestInheritClass getTic() {
       return tic;
   }

   public void setTic(TestInheritClass tic) {
        this.tic = tic;
   }    
}