javascriptfunctionprototypeprototypal-inheritance

Why does Function have both implicit and explicit prototype references, and can I set the implicit reference?


After reading this documentation: http://es5.github.io/#x4.2.1

I was confused by the two prototype references on CF, and by this statement:

The property named CFP1 in CFp is shared by cf1, cf2, cf3, cf4, and cf5 (but not by CF)

Much of the literature on Javascript points out that functions are first class objects, and as such I'd expect to be able to set their implicit prototype reference like an object to achieve prototypal inheritance (disclaimer: I don't actually know what I'd use this inheritance for, but it occurred to me to see if it's possible). Can I set this implicit prototype on a function, or will it always point to Function.prototype (I'm assuming that's the default). And why does Function have both explicit and implicit prototypes? Also do any other types in Javascript have both explicit and implicit prototype references or is Function unique in this regard?


Solution

  • Consider the diagram from the specification, and the code below it, which tries to reproduce what's going on:

    enter image description here

    function CF() {};            // the constructor
    CF.P1 = 'foo';               // P1 is an own property of the constructor; P2 is the same
    var CFp = { CRP1: 'bar' };   // for now, just an object, with a CRP1 property
    CF.prototype = CFp           // set CFp as the 'explicit prototype property' of CF;
                                 // only constructors have such a property
    var cf1 = new CF();          // an instance; 
    var cf2 = new CF();          // another instance; cf3..cf5 are constructed the same way
    Object.getPrototypeOf(cf1);  // CFp; this is the 'implicit prototype link' from cf1 to CFp;
                                 // put another way, CFp became the [[Prototype]] of cf1
    

    You said you were confused by this sentence: the property named CFP1 in CFp is shared by cf1, cf2, cf3, cf4, and cf5 (but not by CF). Consider this:

    cf1.CRP1;   // 'bar' - found on CFp through cf1
    cf2.CRP1;   // 'bar' - found on CFp through cf2
    CF.CRP1;    // undefined
    

    So what that sentence means that you can access the contents of CRP1 from cf1..cf5, but not from the constructor CF (remember, functions/constructors are objects too, thus they can have properties). And that's because CFp (the "owner" of CRP1) is not the [[Prototype]] of CF, it's just the value pointed by the CF.prototype property. The prototype property only exist in function objects, and is used solely to define the [[Prototype]] of instances created by invocations of the function is invoked as a constructor (like in new CF()). The fact that both [[Prototype]] and prototype read as "prototype" is the source of great confusion – and maybe part of what is confusing you; hopefully, it's less confusing now. With that in mind, I'll try to shortly answer your other questions.

    Much of the literature on Javascript points out that functions are first class objects, and as such I'd expect to be able to set their implicit prototype reference like an object to achieve prototypal inheritance [...].

    In ES5 there is no way to directly set the implicit prototype reference (or [[Prototype]]) of an existing object, except for the non-standard __proto__ property. What you can do is create new objects with a given [[Prototype]]. You can do that with var obj = new ConstructorFunction(), where the [[Prototype]] of obj is ConstructorFunction.prototype, or with var obj = Object.create(someOtherObj), where the [[Prototype]] of obj is someOtherObj. The later versions of the language introduced Object.setPrototypeOf to do that, but its use is discouraged for performance reasons[still current? citation needed].

    Can I set this implicit prototype on a function, or will it always point to Function.prototype (I'm assuming that's the default).

    Yes, with __proto__ or Object.setPrototypeOf. But usually you shouldn't.

    And why does Function have both explicit and implicit prototypes? Also do any other types in Javascript have both explicit and implicit prototype references or is Function unique in this regard?

    Function ("the Function constructor") is just a function, and as any other function it has a prototype property; it's also an object, and as (almost) any other object is has a [[Prototype]] object. There are standard constructors for other types too, like Object, String, Array, Boolean, Number. They're all functions, and have both a prototype and a [[Prototype]].