Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Function.prototype is a function

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?)
like image 823
html_programmer Avatar asked Oct 04 '15 00:10

html_programmer


People also ask

What is a prototype of a function?

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

Is function definition same as function prototype?

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.

What is function prototyping in C?

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.

Why is function prototype required?

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.


3 Answers

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
    
like image 137
Oriol Avatar answered Oct 16 '22 21:10

Oriol


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.
like image 7
Alvin Pascoe Avatar answered Oct 16 '22 21:10

Alvin Pascoe


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.

like image 3
traktor Avatar answered Oct 16 '22 21:10

traktor