I'm digging into the Javascript prototype chain.
In order to document my findings, I've drawn the following scheme:
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:
Function.prototype
to be of type function, instead of object?typeof Function.prototype; //"function"
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?) A function prototype is a definition that is used to perform type checking on function calls when the EGL system code does not have access to the function itself. A function prototype begins with the keyword function, then lists the function name, its parameters (if any), and return value (if any).
While a function definition specifies how the function does what it does (the "implementation"), a function prototype merely specifies its interface, i.e. what data types go in and come out of it.
A function prototype is simply the declaration of a function that specifies function's name, parameters and return type. It doesn't contain function body. A function prototype gives information to the compiler that the function may later be used in the program.
The function prototypes are used to tell the compiler about the number of arguments and about the required datatypes of a function parameter, it also tells about the return type of the function. By this information, the compiler cross-checks the function signatures before calling it.
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:
Object.prototype
is an Object object.Function.prototype
is a Function object which returns undefined
when invoked.Array.prototype
is an empty Array object.String.prototype
is a String object whose value is an empty String.Boolean.prototype
is a Boolean object whose value is false
.Number.prototype
is a Number object whose value is +0
.Date.prototype
is a Date object whose [[PrimitiveValue]] is NaN
.RegExp.prototype
is a RegExp object whose data properties are like new RegExp()
's ones.Error.prototype
is an Error object.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:
Function.prototype
is itself a built-in function object.Array.prototype
is an Array exotic object and has the internal methods specified for such objects.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
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With