In JavaScript, Why Typeof Function.Prototype Is "Function", Not "Object" Like Other Prototype Objects

In JavaScript, why typeof Function.prototype is function, not object like other prototype objects?

This seems to be defined in ECMAScript 5:

15.3.4 Properties of the Function Prototype Object

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

why typeof(Function.prototype) is function

From the specification:

The Function prototype object is the intrinsic object %FunctionPrototype%. The Function prototype object is itself a built-in function object. When invoked, it accepts any arguments and returns undefined. It does not have a [[Construct]] internal method so it is not a constructor.

NOTE

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.

(my emphasis)

If we go to the ES5 spec, it says:

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

...without offering any explanation for why that would be the case. That language is essentially unchanged in ES1, ES2, ES3, and ES5. I think the original idea was basically that that was what gave it its function-ness, although typeof (even in ES1) didn't look at the internal [[Class]], it looked at whether the thing implemented [[Call]] (as it still does). When something goes back all the way to ES1, one frequently has to just invoke the "because Eich did the first JavaScript in 10 days and yeah, weird stuff happens when you do that" argument. :-)


Side note: By "object literal" I take it you mean "plain object." (An "object literal" — what the specifiation calls an object initializer — is just a way to write an object in source code. There are other ways to create plain objects.)

Function.prototype is a function

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

ES 5 - Why Function.prototype object does not have property prototype?

Why function type object Function.prototype does not have its own property prototype, in the below visualisation?

Why would it, what would you expect it to point to? It's not a constructor function. (In fact, with ES6, many functions don't have a .prototype method).

It's rather quite weird that Function.prototype is a callable object (function), there's actually no benefit from that. It could've been a plain object just as well. "The spec said so" is all we have (see also these three questions).

Any object usually inherits from an object type object, but the Function object and Person object inherit from function type object Function.prototype?

Well yes, why wouldn't they? As you say yourself, functions are just objects, and every object inherits from another object (or null). That some objects are callable (and typeof yields "function" for them) does not make any difference for inheritance.

Is Object.prototype an object and is prototype somehow not?

If an Object has a prototype, the prototype is also an Object. These Objects all chain together until you reach null, which is the end of the prototype chain.

// Object is a constructor
Object.prototype; // Object {}, will be the prototype of `new Object`s
// Object.prototype is an Object
Object.getPrototypeOf(Object.prototype); // null, we are at the end of the chain

You should also note, however, that you can't keep accessing the obj.prototype property, as this is only applicable to Constructors, consider

function Foo() {
}
Foo.prototype; // Foo {}
// vs
(new Foo).prototype; // undefined

The correct way to find the prototype of an Object is by using Object.getPrototypeOf(obj),

Object.getPrototypeOf(new Foo) === Foo.prototype; // true

It may also be of note that legacy browsers may not support Object.getPrototypeOf, in which case many offer the property obj.__proto__. However, try to avoid using __proto__ outside of a shim for such browsers if you need to access the prototype chain.


Finally, using new with a Constructor isn't the only way to create this chain, you can set them up using Object.create

var a = Object.create(null),
b = Object.create(a), // b will inherit from a
c = Object.create(b); // c will inherit from b, hence also a
a.foo = 'foo';
b.bar = 'bar';

a instanceof Object; // false

a.bar; // undefined
c.foo + c.bar === 'foobar'; // true

Also consider

c.prototype; // undefined
// vs
Object.getPrototypeOf(c) === b; // true

Is `Object` a function in JavaScript?

You seem to be confused between "object" (the data structure) and Object (the function).

An object is a concept in JavaScript that is a generic container for some data. An object contains properties with keys and associated values.

In JavaScript, everything that is not a primitive is an object. This includes functions, which are basically a special type of object that can be "called" with the () syntax.

JavaScript provides a number of built-in functions that have various purposes. Two such functions happen to be called Object and Function. So in other words Object is a function and thus also an "object" (data structure).

Let's take your function Foo as an example:

function Foo() {
var a = "3";
}

Foo is a function. This means that Foo can be called, eg. var f = Foo(). In this case f will be undefined since Foo doesn't return anything.

Because Foo is a function, it is also an object. This means we can also add and read properties from it:

Foo.bar = 5;
Foo.bar++;
console.log(Foo.bar); // prints 6

Please note that this "object" part of Foo is not related to the contents of the function. That means that the code you declared (var a = "3") is irrelevant. You cannot access var a in any way here because it does not exist until you call the function. If you were to do Foo.a, you are not manipulating var a inside the function; you are working with the property a on the object Foo.

You can however do it the other way around and access properties on Foo inside of the function:

function Foo() {
var a = "3"; // a is local to this scope, you cannot get to it from outside
console.log(a); // prints 3 - local variable a is accessible inside the scope of this function
console.log(Foo.a); // prints 5 - a is a property on object Foo, and is accessible here
}
// var a inside Foo cannot be accessed here
Foo.a = 5;
Foo();

Edit: Re. your question regarding "this" in the comments. this is a special keyword in JavaScript that refers to an object. However, this object is not the function itself, it is a new object that is created when you call a function using the new keyword:

function Bar() {
this.a = 10;
console.log(this == Bar); // prints false
}
var bar = new Bar();
console.log(bar.a); // prints 10

A function that is meant to be called with the new keyword is referred to as a "constructor function". Object and Function are both examples of constructor functions, which is why their names start with an uppercase letter (a convention in JavaScript).

When you create an object with a constructor function, the property prototype of this function is used as the prototype (accessible through __proto__) of the created object.

console.log(bar.constructor == Bar) // prints true
console.log(bar.__proto__ == Bar.prototype) // prints true

this is also used for other things, but that is a broad subject and way out of scope for this question.



Related Topics



Leave a reply



Submit