2

I'm discerning the famous JS's extend function:

function extend(parent, child) {
    var F = new Function();
    F.prototype = parent.prototype;
    child.prototype = new F();
    //child.prototype.constructor = Child
}

I've commented out the line where the constructor property is overridden. If I create the objects in the following way:

var p = function p() {this.parent = true};
var c = function c() {this.child = true};
extend(p, c);
console.log((new c()).constructor); //outputs reference to `p` constructor, why?

My question is why the constructor references var p = function p() {this.parent = true}; instead of anonymous function that variable F points to? The manual on constructor property states that constructor property returns a reference to the Object function that created the instance's prototype and the instance prototype new F() was created by anonymous function. So why does it point to the other thing?

Update

Is the following assumption I've made based on HMR's answer correct?
When a new function is created, JS engine creates new empty object and sets prototype proprety of the function to point to this newly created object. Then JS engine adds a constructor property to this object pointing to the created function.

So now to my case. When var F = new Function() JS engine:
1) creates new F function
2) creates new object (let it be oF)
3) sets prototype F.prototype = oF
4) sets constructor oF.constructor = F

The same goes for function p which has its prototype oP with a constructor property set to function p. As we remember, F.prototype points to oF but after we execute the following line F.prototype = parent.prototype it is now points to oP. Then when we access (new c()).constructor property new c() object doesn't have it so JS engine starts looking backwards on prototypes and finds the property on oP pointing to function p - and this is excatly what I get.

Max Koretskyi
  • 101,079
  • 60
  • 333
  • 488

3 Answers3

2

If a member can't be found on an instance then JS engine will look in the prototype chain. The prototype chain is a bunch of objects that have a __proto__ of another object until it's null.

Please note that __proto__ is a non standard property and used here to explain the object first used in the prototype chain.

Since you replaced child.prototype with an empty object that has __proto__ of parent.prototype the JS engine cannot find constructor on c, child.prototype but then it'll find it on child.prototype.__proto__ which is parent.prototype.

When you do:

child.prototype.constructor=child;

it adds it to child.prototype so it doesn't mutate parent.prototype which is in child.prototype.__proto__. This is called shadowing.

More on prototype and constructor functions here.

[update]

The prototype chain after inherits looks like this:

insance of child (new c())
  => c.prototype === new F() so an empty object
    => p.prototype, set when you did F.prototype=parent.prototype
Community
  • 1
  • 1
HMR
  • 37,593
  • 24
  • 91
  • 160
  • 1
    Thanks, please see an update to my question. Could you please check that my understanding is correct? – Max Koretskyi Aug 22 '14 at 07:32
  • Thanks! Yeah, I didn't mention that since I understand that :). Best! – Max Koretskyi Aug 22 '14 at 09:10
  • @Maximus Correct, you forgot to mention that setting child.prototype to an instance of F (new F()) basically sets the child prototype to an empty object (with no constructor property) with it's `__proto__` pointing to parent.prototype. That is why you can safely mutate child.prototype without affecting parent.prototype because parent.prototype is one level higher in the chain. Updated my answer to show the prototype chain and where it came from – HMR Aug 22 '14 at 09:10
0

You should put

F.prototype = parent;

That way you did you're are saying "both constructor are the same", with the example I show, you will say "F constructor is equal parent constructor"

paulojean
  • 113
  • 1
  • 6
  • no, that's wrong. If parent has static properties then they'll end up in child.prototype plus it doesn't solve nor explain the problem – HMR Aug 21 '14 at 15:05
0

The problem is that the child constructor is effectively lost during that process.

function extend(parent, child) {
    // Create a completely blank anonymous function
    var F = new Function();

    // Give it all the same properties as the parent, including constructor
    F.prototype = parent.prototype;

    // Overwrite the childs prototype with an instance of the parent
    child.prototype = new F();
}

Since the child prototype is overwritten with an instance of parent.prototype, child.prototype.constructor is overwritten as well.

Personally, here's the extend function I use and it works beautifully.

function extend(Derived, Base) {
    Derived.prototype = Object.create(Base.prototype);
    Derived.constructor = Derived;
}
Mike Cluck
  • 31,869
  • 13
  • 80
  • 91
  • `Since the child prototype is overwritten with an instance of parent.prototype` if that were true then setting `child.prototype.constructor` would set `parent.prototype.constructor` to be correct: child.prototype is set to an empty object who's `__proto__` is parent.prototype. – HMR Aug 21 '14 at 15:18
  • @HMR Sure, if you want to get pedantic about it. `__proto__` is still on the `child` prototype, even if it isn't the first link in the chain. This is still a valid explanation of the situation. – Mike Cluck Aug 21 '14 at 15:24
  • child prototype is overridden with an instance of F that has a prototype of parent.prototype. If child.prototype is set to be parent.prototype you could never mutate child.prototype without affecting parent.prototype and the statement `Since the child prototype is overwritten with an instance of parent.prototype` suggest child.prototype === parent.prototype parent.prototype cannot have instances either since it already is an instance. – HMR Aug 21 '14 at 15:26
  • @HMR Setting `child.prototype = parent.prototype` wouldn't prevent you from mutating child.prototype. It just means that mutating it would mutate the parent. You're right that the prototype is an empty object who's prototype points to parent.prototype. But I figure providing this level of detail to OP clouds the true cause behind why `child.constructor` does not point to `child`: because it has been overwritten on the prototype. – Mike Cluck Aug 21 '14 at 15:30