this.constructor.prototype — can't wholly overwrite, but can write individual props?

TL;DR? Why can't I overwrite a constructor's prototype from within the constructor?

I'm figuring out my pattern for prototypical inheritance. I don't like how prototypes are usually defined externally from a constructor, and want to logically encapsulate things better.

I found that the one magical line that I expected to work, doesn't.

function Orifice(){
  this.exhaust=function(){};
  this.ingest=function(){};
}
var standardOrifice = new Orifice();

function Sphincter(){
  this.constructor.prototype = standardOrifice; // <-- does not work
  this.relax=function(){};
  this.tighten=function(){};
}

Interestingly, I can write individual properties to this.constructor.prototype, but I cannot overwrite the whole prototype object the same way one can outside of a constructor's definition.

So stuff like this works:

  this.constructor.prototype.exhaust = standardOrifice.exhaust;
  this.constructor.prototype.ingest = standardOrifice.ingest;

For which I can create a simple clone function to handle this:

function extend(target){
  return {
    from: function(obj){
      target.__proto__ = obj.constructor.prototype;
      for (key in obj) if (obj.hasOwnProperty(key)) target[key]=obj[key];
      return target;
    }
  };
}

Thankfully in my tests so far, this technique appears to work well, though I'm not sure if there are details or performance cases I could be missing.

function Sphincter(){
  extend(this.constructor.prototype).from(standardOrifice);
  //...
}

Why can't I overwrite a constructor's prototype from within the constructor? Yet I can outside the constructor? And writing properties individually works from within a constructor?

Answers:

Answer

Answer to the Specific Question

Why can't I overwrite a constructor's prototype from within the constructor?

It's because constructors are actually called after your object has already been instantiated. And since your object has managed to instantiate before your constructor has touched anything, your constructor has also already been assigned a "default" prototype.

Adding properties to this.constructor.prototype seems to work -- because you're actually manipulating the constructor's pre-assigned default prototype object, which all of your instances inherit from.

In my examples, this.constructor.prototype ended up referring to the default-assigned prototype of the constructor: so wholly overwriting it meant all new instances from that moment onward would have that new prototype -- as Bergi said, "too late" -- your current instance would not have that new prototype, as it still has the old default-assigned prototype because it's already been instantiated.

A Better Pattern for Avoiding Nonsense

I've come to understand, that the techniques presented in my question simply won't do. The question itself is generally misguided. By combining Bergi's wisdom with my own personal biases, I've come up with this pattern as a means to avoid having to find an answer to the original question altogether:

function extend(p){
  return { to: function(C){ for (k in p) if (p.hasOwnProperty(k)) 
  C.prototype[k]=p[k]; return C; } };
};

var orifice = new function Orifice(){
  this.exhaust=function(){};
  this.ingest=function(){};
};

var Sphincter = extend(orifice).to(function Sphincter(){
  this.relax=function(){};
  this.tighten=function(){};
});


Here's the extend function, expanded:

function extend(parentObject){
  return { 
    to: function(ChildConstructor){
      for (key in parentObject) 
        if (parentObject.hasOwnProperty(key)) 
          ChildConstructor.prototype[key] = parentObject[key];
      return ChildConstructor;
    }
  };
};

I used this to test that it works:

// TESTING
var s=new Sphincter();
var tests=['relax','tighten','exhaust','ingest'];
for (var i in tests) console.log("s."+tests[i]+"() is "+(tests[i]in s?"present :)":"MISSING!"));
Answer

Why can't I overwrite a constructor's prototype from within the constructor?

You can, but it's too late. The new instance has already been generated, inheriting from the old prototype. Maybe read how new works.

I don't like how prototypes are usually defined externally from a constructor.

That's just the way it is. You really should not setup the prototype from within the constructor - it would be executed everytime a new instance is created. That's specifically what prototypes are not supposed to be. See also Assigning prototype methods *inside* the constructor function - why not?

and want to logically encapsulate things better.

You might want to have a look at the various (revealing) module patterns. Or maybe even at some Class framework.

I'm currently looking for more concrete reasons that I should not go forth with the pattern I've been presenting.

It does not work in Internet Explorer. It would not work in any ES5-compliant environment that does not support the __proto__ property. You should never use it set a prototype on an existing object. Instead, use Object.create (or its shim) for Correct javascript inheritance - which requires that you overwrite the prototype outside of the constructor.

My suggestion is to call your extend helper outside the constructor on it, which still has a nice syntax.

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.