Hello and welcome to our community! Is this your first visit?
Register
Enjoy an ad free experience by logging in. Not a member yet? Register.
Results 1 to 7 of 7
  1. #1
    Regular Coder
    Join Date
    Jan 2013
    Location
    Germany
    Posts
    578
    Thanks
    4
    Thanked 77 Times in 77 Posts

    OOP – create instance of same type

    Hi everyone,

    up ahead: this question was also asked on stackoverflow, where so far I didn't get any response. I'm hoping someone here can help me out.

    Say I have a class BaseClass and two classes ClassA and ClassB which inherit from BaseClass via

    Code:
    ClassA.prototype = Object.create( BaseClass.prototype );
    I want to write a method that, if called from an instance of ClassA, returns a new instance of ClassA and if called from an instance of ClassB, returns a new instance of ClassB – and I'm wondering what the best way is to do so.
    Currently, after fiddling around, I came up with the following solution, which seems to work just fine:

    Code:
    // this is added additionally to the above mentioned inheritance setup
    ClassA.prototype.constructor = ClassA;
    ClassB.prototype.constructor = ClassB;
    
    BaseClass.prototype.newInstance = function () {
        var constructor = this.constructor,
            ClassFactory = constructor.bind.apply( constructor, [constructor].concat( [].slice.call( arguments ) ) );
    
        return new ClassFactory();
    };
    The obvious downside is to overwrite the prototype's constructor, which feels wrong, because the prototype's constructor in fact was BaseClass.

    Another way that should work is the approach of overriding:

    Code:
    BaseClass.prototype.newInstance = function () {
        throw new Error( 'Cannot call abstract method.' );
    };
    
    /** @override */
    ClassA.prototype.newInstance = function () {
        var ClassFactory = ClassA.bind.apply( ClassA, [ClassA].concat( [].slice.call( arguments ) ) );
    
        return new ClassFactory();
    };
    
    // ... do the same for ClassB
    Now my question would be asking for some insight on which strategy is better and why, what are up- and downsides to each and maybe even if there is a better way?

    To clarify what the goal is, I want to end up doing something like this:

    Code:
    BaseClass.prototype.someMethod = function () {
        var instance = this.getInstance();
        instance.someMethod();
    
        return instance;
    };
    Now, since ClassA and ClassB will inherit this method, I want it to return a new instance of the same class that it was called from.
    Last edited by Airblader; 04-20-2013 at 04:56 PM.

  • #2
    Supreme Master coder! Old Pedant's Avatar
    Join Date
    Feb 2009
    Posts
    27,572
    Thanks
    80
    Thanked 4,620 Times in 4,583 Posts
    Code in Java, instead?

    Sorry, I couldn't resist.

    *Personally*, I like the override scheme better, just from a theoretical standpoint. But man, whether that's really the better way when it comes to JavaScript... I guess I'd certainly try it that way and see what then doesn't work right, if anything.
    An optimist sees the glass as half full.
    A pessimist sees the glass as half empty.
    A realist drinks it no matter how much there is.

  • #3
    Senior Coder rnd me's Avatar
    Join Date
    Jun 2007
    Location
    Urbana
    Posts
    4,460
    Thanks
    11
    Thanked 600 Times in 580 Posts
    i don't like stepping on constructors, or repeating Constructor names inside the constructor; makes it hard to alter later because there's an intrinsic dependency.

    imho, it's better to uses a single extra common call during construction. This can decorate the instance in many ways, and offers a third path along prototype inheritance and in-constructor definition. I call it peg, but you can use it for many things and call it whatever makes sense to your understanding.

    i like to pass and return this to my helper, so it's not much more instance code than defining a single property is.

    using this pattern, it's a breeze to do what i'm pretty sure you're trying to do, provide the missing parent class link that javascript forgot:

    Code:
    function peg(that){ // factory helper to upgrade all instances from within the constructor.
       that.super=arguments.callee.caller; // links the argument to the function its called from.
      return that;
    }
    
    //common functionality is defined by the base:
    function BaseClass(){ this.prop=123; };
    BaseClass.prototype.someMethod = function () {
        var instance = new this.super();
        instance.cloned=true;
        return instance;
    };
    
    //define some lowly classes, with a peg'd super, own name, and inherited prop property:
    function ClassA(){ peg(this).name="a";   }
    function ClassB(){ this.super=arguments.callee; this.name="b";   } //typing it out...
    
    //subscribe the prototype of subclasses with base class:
    ClassA.prototype= new BaseClass(); //old-school 
    ClassB.prototype = Object.create(BaseClass.prototype); // ecma5 ver of old-school
    
    
    //now let's kick the tires:
    var a=new ClassA(); // new instance of composite class (ClassA<BaseClass)
    var a2=a.someMethod(); // run cloner defined in common prototype, BaseClass
    
    a2.name="d"; //alter the cloned instance
    
    [a2.name, a.name, a.prop, a2.prop]  // examine the both names and check inheritance:
     //shows: ["d", "a", 123, 123] // yay!
    you can also pass more arguments in the peg() or someMethod() calls to further customize the instance.
    Last edited by rnd me; 04-21-2013 at 06:22 AM.
    my site (updated 2014/10/20)
    BROWSER STATS [% share] (2014/9/03) IE7:0.1, IE8:4.3, IE11:9.2, IE9:2.7, IE10:2.6, FF:16.8, CH:47.5, SF:7.8, NON-MOUSE:37%

  • #4
    Regular Coder
    Join Date
    Jan 2013
    Location
    Germany
    Posts
    578
    Thanks
    4
    Thanked 77 Times in 77 Posts
    Quote Originally Posted by Old Pedant View Post
    Code in Java, instead?
    I would! If only it wouldn't defeat the purpose of what I'm doing.

    I guess I'd certainly try it that way and see what then doesn't work right, if anything.
    Maybe I was unclear about the question. Both methods will work just fine, but I was wondering about downsides like a hidden memory leak, an extreme performance killer etc. – and if there might be another, better way.

  • #5
    Regular Coder
    Join Date
    Jan 2013
    Location
    Germany
    Posts
    578
    Thanks
    4
    Thanked 77 Times in 77 Posts
    @ rnd_me

    Thanks for your solution. What I don't like about it is that in terms of ECMAScript 5, it is deprecated and will fail the strict mode test. I like to work more "modern" and rather ignore browsers that are or should be dead.
    However, I happen to have a class for helper functions, so using that class and passing the this argument would be another idea. The peg idea to get rid of the internal dependency is quite nifty, if it weren't for the deprecation.

    I think so far I still favor the first solution I had in my post, seeing how I don't have an internal dependency of the class name in there. The only thing I'm required to do is to manually set the constructor, which I don't see as a downside as it makes sense and happens right when I declare the ineritance (which is an essential step).

    The thing that concerns me the most at the moment is potential problems that I'm overlooking. Other than that I like the solution because it feels Java-ish and clean to me – and yes, I like Java.

    By the way: the "old school" and "ecmascript5 version of old school" you mention do in fact do different things.

  • #6
    Senior Coder rnd me's Avatar
    Join Date
    Jun 2007
    Location
    Urbana
    Posts
    4,460
    Thanks
    11
    Thanked 600 Times in 580 Posts
    Quote Originally Posted by Airblader View Post
    @ rnd_me

    Thanks for your solution. What I don't like about it is that in terms of ECMAScript 5, it is deprecated and will fail the strict mode test. I like to work more "modern" and rather ignore browsers that are or should be dead.
    However, I happen to have a class for helper functions, so using that class and passing the this argument would be another idea. The peg idea to get rid of the internal dependency is quite nifty, if it weren't for the deprecation.
    you can set the property in the constructor by name if you want to be stricter, the functionality is the same. ex: this.super=ClassB;

    what it boils down to is whether you want to trust the .constructor property, or an explicitly set property of your choosing.

    i don't trust constructor, for well-documented reasons.
    as you show, it's not always what you think it is or what it started out being. you might as well avoid the potential collision, and use your own property name:

    Code:
    Object.defineProperty(ClassA.prototype, "super", {value: ClassA});
    and then you can use your first solution with just the name change.

    to be sure, using constructor can often work, and you may find it suits your needs, but all you're really doing is substituting "this.constructor" for "this.super" in the newInstance code.

    in this case, constructor is just a prop, kinda like:
    Code:
    String.prototype.constructor=Array;
    which doesn't really do anything to Strings or Arrays...

    one subtle advantage to using some other property name is that constructor still has it's native behavior, but if you step on it, that link is gone...

    one last unrelated and hopefully temporary observation: bind() really slows down V8 for some reason, 10-50X hits compared to apply()...
    Last edited by rnd me; 04-21-2013 at 11:04 AM.
    my site (updated 2014/10/20)
    BROWSER STATS [% share] (2014/9/03) IE7:0.1, IE8:4.3, IE11:9.2, IE9:2.7, IE10:2.6, FF:16.8, CH:47.5, SF:7.8, NON-MOUSE:37%

  • #7
    Regular Coder
    Join Date
    Jan 2013
    Location
    Germany
    Posts
    578
    Thanks
    4
    Thanked 77 Times in 77 Posts
    Not trusting certain properties or objects is a good point. Afterall, there's a reason why jQuery is wrapped inside

    Code:
    (function( window, undefined ) { ... })( window );
    But for now I'll ignore that problem. If there are any js-internal problems with the constructor property, I'd be happy about a reference.

    one last unrelated and hopefully temporary observation: bind() really slows down V8 for some reason, 10-50X hits compared to apply()...
    That's something I didn't know before – and something I'll have to look into. Thanks!


  •  

    Tags for this Thread

    Posting Permissions

    • You may not post new threads
    • You may not post replies
    • You may not post attachments
    • You may not edit your posts
    •