28

I'm digging into the Javascript prototype chain.
In order to document my findings, I've drawn the following scheme:

enter image description here

Although most of the concepts are clear, I'm left with just two related questions. Rather than splitting them up, I guessed that centralising them in this question might be better:

  1. Is there a reason for Function.prototype to be of type function, instead of object?
    typeof Function.prototype; //"function"
  2. Is Function.prototype a 'unique function' in JS since it doesn't have a prototype property of its own like other functions do? (is there a generally accepted 'name' to refer to it?)
html_programmer
  • 18,126
  • 18
  • 85
  • 158
  • notice that `function Person (){} typeof Person.prototype //"object"` – Saar Oct 04 '15 at 00:27
  • 1
    functions are also objects –  Oct 04 '15 at 00:29
  • 2
    Object.getPrototypeOf(Function.prototype) === Object.prototype –  Oct 04 '15 at 00:29
  • This reference from mozilla might help : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects –  Oct 04 '15 at 00:43
  • 1
    Oh, sorry, I got confused by the wording of your question. I thought that by "doesn't have a prototype" you were talking about `[[Prototype]]` internal member. Now I see you were referring to the `prototype` property. –  Oct 04 '15 at 00:51
  • @StefanBaiu Oh I see what you mean, I rephrased, should have mentioned property. – html_programmer Oct 04 '15 at 00:57
  • 3
    Possible duplicate of [In JavaScript, why typeof Function.prototype is "function", not "object" like other prototype objects?](http://stackoverflow.com/questions/4859308/in-javascript-why-typeof-function-prototype-is-function-not-object-like-ot) – Roy Miloh Oct 04 '15 at 01:04
  • 1
    That question, explains why it is a function not why they chose it to be function instead of an object. – MinusFour Oct 04 '15 at 01:10

3 Answers3

27

The reason is that the ES5 spec says so:

The Function prototype object is itself a Function object (its [[Class]] is "Function") that, when invoked, accepts any arguments and returns undefined.

Note it's common in ES5 to make the prototype of some class a member of that class:

I think it was standardized as such because the prototype of a class has the intrinsic properties of that class, as the instances of that class. And if it looks like a duck it should behave as a duck. So calling the methods of the prototype on the prototype itself instead of on an instance should work too.

However, ES6 didn't like this. So it changed the behavior for those:

  • Boolean.prototype is an ordinary object with no [[BooleanData]] internal slot.
  • Error.prototype is an ordinary object with no [[ErrorData]] internal slot.
  • Number.prototype is an ordinary object with no [[NumberData]] internal slot.
  • Date.prototype is an ordinary object with no [[DateValue]] internal slot.
  • String.prototype is an ordinary object with no [[StringData]] internal slot.
  • RegExp.prototype is an ordinary object with no [[RegExpMatcher]] nor any of the other internal slots of RegExp instance objects.

And also for new "classes" (ES6 objects no longer have a [[Class]]):

  • Symbol.prototype is an ordinary object with no [[SymbolData]] internal slot.
  • TypedArray.prototype is an ordinary object with no [[ViewedArrayBuffer]] nor any other of the internal slots that are specific to TypedArray instance objects.
  • Map.prototype is an ordinary object with no [[MapData]] internal slot.
  • Set.prototype is an ordinary object with no [[SetData]] internal slot.
  • WeakMap.prototype is an ordinary object with no [[WeakMapData]] internal slot.
  • WeakSet.prototype is an ordinary object with no [[WeakSetData]] internal slot.
  • ArrayBuffer.prototype is an ordinary object with no [[ArrayBufferData]] nor [[ArrayBufferByteLength]] internal slots.
  • DataView.prototype is an ordinary object with no [[DataView]], [[ViewedArrayBuffer]], [[ByteLength]], nor [[ByteOffset]] internal slots.
  • GeneratorFunction.prototype is an ordinary object with no [[ECMAScriptCode]] nor any other of the internal slots listed in Table 27 or Table 56.
  • Promise.prototype is an ordinary object with no [[PromiseState]] nor any of the other internal slots of Promise instances.

However, the old behavior remains for those:

So now the reason is backwards compatibility:

The Function prototype object is specified to be a function object to ensure compatibility with ECMAScript code that was created prior to the ECMAScript 2015 specification.

Note this doesn't make Function.prototype a special function. Only constructors have the prototype property:

Function instances that can be used as a constructor have a prototype property.

There are multiple examples of non-constructor functions apart from Function.prototype, such as

  • Methods in Math object:

    typeof Math.pow; // "function
    'prototype' in Math.pow; // false
    
  • Some host objects:

    typeof document.createElement('object'); // "function
    'prototype' in document.createElement('object'); // false
    
  • In ES6, arrow functions:

    typeof (x => x * x); // "function
    'prototype' in (x => x * x); // false
    
Oriol
  • 274,082
  • 63
  • 437
  • 513
8

In answer to your questions:

1) Function.prototype is a type of function because, according to ECMAScript 2015:

The Function prototype object is the intrinsic object %FunctionPrototype%. The Function prototype object is itself a built-in function object.

The Function prototype object is specified to be a function object to ensure compatibility with ECMAScript code that was created prior to the ECMAScript 2015 specification.

So the Function prototype object is only defined as a Function object to ensure compatability with older ECMAScript standards. The function doesn't actually do anything:

When invoked, it accepts any arguments and returns undefined.

http://www.ecma-international.org/ecma-262/6.0/#sec-properties-of-the-function-prototype-object

2) Regarding the prototype property:

The Function prototype object does not have a prototype property.

Same Source

This is unique since all functions usually possess a prototype property, however since the Function prototype object is only specified as a Function object to maintain compatability, it's behaviour is unlike that of regular functions.

I've created a JSFiddle with various tests in case it helps anyone:

http://jsfiddle.net/Ld0b39xz/

// We'll use 'Object.getPrototypeOf' to access [[prototype]]

// As you know, [[prototype]] of Object.prototype returns 'null'. 
console.log(Object.getPrototypeOf(Object.prototype));
// null

////////////////////////////////////////////////////////

// Let's take a closer look at Function.prototype
console.log(Function.prototype);
// Output:
// function(){}

// This is what the specs say should happen:
// "The Function prototype object is itself a built-in function object."

/////////////////////////////////////////////////////

// Let's see if this function has a 'prototype' property.
// All functions normally have a prototype property that initially
// references an empty object...except this one.
var fn = Function.prototype;
console.log(fn.prototype);
// Output:
// undefined

// This is expected, according to the specs:
// "The Function prototype object does not have a prototype property."

// It does have some properties such as 'name' and 'length',
// but not 'prototype'.

////////////////////////////////////////////////////////

// Let's see what [[prototype]] of Function.prototype returns.
console.log(Object.getPrototypeOf(Function.prototype));
// Output:
// Object{}

// Again this is expected:
// "The value of the [[Prototype]] internal slot of the
// Function prototype object is the intrinsic object %ObjectPrototype%"

/////////////////////////////////////////////////////////

// Now lets see what the [[Prototype]] of this object is:
console.log(Object.getPrototypeOf(Object.getPrototypeOf(Function.prototype)));
// Output:
// null

// We've come full circle since all the statement above is
// doing is looking for the prototoype of the native Object,
// which we already know is 'null' from our first test.
Community
  • 1
  • 1
Alvin Pascoe
  • 1,189
  • 7
  • 5
3

In replacement of a previous answer which I could not stand by. With thanks to Oriol. The head scratching is mine.

In regards the first question the Function object it not particularly different simply because Function.prototype is a function. Other built in constructors use prototype objects of their own type. What draws attention to the Function case is that the typeof operator treats function objects diffently to other objects by returning "function" instead of "object".

Global constructors listing themselves as constructors of their prototype objects:

var BuiltIn = Function; // for example
BuiltIn.prototype.constructor == BuiltIn // true

is more or less documentary. Prototype objects of built in constructors generally have methods which interface with the javascript engine and are not created using a javascript call to their listed constructor as it appears at run time: Function.prototype instanceof Function is false with similar results for other built in constructors such as Array, RegExp etc tested.

The global Function object is unique, however, in that it lists itself as is its own constructor (Function.constructor == Function is true), and that it is an instance of itself (Function instanceof Function is true as well). The latter result indicates that Function.prototype is in the prototype chain of Function. Function.prototype itself is prototyped on Object.prototype.

Another reason to think Function.prototype is not a Function object in the usual sense (apart from saying so in the documentation) is that it cannot be called as a constructor and throws an error if an attempt is made to do so. Since the prototype property of a function is used when the function is called as a constructor, it makes sense for Function.prototype not to have this property.

traktor
  • 17,588
  • 4
  • 32
  • 53
  • Many other built-in JavaScript functions and arrow functions cannot be called as constructors. – Melab Jun 11 '22 at 19:35