James Cadd
James Cadd

Reputation: 12216

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?

Upvotes: 6

Views: 1908

Answers (2)

bfavaretto
bfavaretto

Reputation: 71939

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]].

Upvotes: 10

mdajobs
mdajobs

Reputation: 1

The implicit reference is set by the platform whenever a new instance is created by calling a constructor function, or class with the new keyword, or passing an object with shared members to Object.create method, adding the input parameters to the object and returning it from a factory function. The explicit prototype property, as it's called in ECMA-262 spec, is set by specifying the function for the reference: .prototype.constructor. The prototype object defined on this function is what defines inherited properties and sets the prototype chain. Here is an example using a factory function (my preference):

function range(from, to) {

let r = Object.create(range.methods);

r.from = from;
r.to = to;

return r;

}

range.methods = {

includes(x) {
    return this.from <= x && x <= this.to;
},

*[Symbol.iterator]() {
    for (let x = Math.ceil(this.from); x <= this.to; x++) yield x;
},

toString() {
    return "(" + this.from + "..." + this.to + ")";
}

};

The prototype in the chain, which establishes inheritance, is held by the internal property: [[prototype]] which can be referenced with __proto__ or Object.getPrototypeOf method. In Chrome console: Object.getPrototypeOf(Array).__proto__ will produce the prototype of Array. Inspecting this object will expose "Object" as the value of the constructor property. Furthermore, it will show that its __proto__ is null.

BTW, Figure 1: Object/Prototype Relationships [https://262.ecma-international.org/14.0/#sec-objects], is a little misleading. The prototype object on CFp, is buried within CF.prototype. You have to follow CF.prototype.constructor to the CFp function, then to its prototype property. Using the example for Array above, that would be Array.prototype.constructor. This is a reference to the Object constructor function.

Upvotes: 0

Related Questions