Using JavaScript, is it permissible to use a function as a prototype, e.g.
function Foo(){
}
Foo.prototype = function(){
}
or is it required to use
Foo.prototype = { };
and more importantly, does it make an important difference?
Using JavaScript, is it permissible to use a function as a prototype, e.g.
function Foo(){
}
Foo.prototype = function(){
}
or is it required to use
Foo.prototype = { };
and more importantly, does it make an important difference?
Yes both ways are permissible because of flexibility nature of JavaScript prototypical inheritance. First let's look at the prototypical chain for the both cases. I am considering only literal objects for the simplicity.
The last value of __proto__
(internal [[Prototype]]
) for the objects created using literal is Object.prototype
eventually. For example:
/*Example 1*/
var o = { foo: 1, bar: 2 }
Prototypical chain: o -> Object.prototype
/*Example 2*/
var o = { foo: 1, bar: 2 }
o.__proto__ = { another:3 }
Prototypical chain: o ->o.__proto__ -> Object.prototype
For functions, the last value of __proto__
is Object.prototype
as well except it has always a preceding value Function.prototype
. For example:
/*Example 1*/
function Foo () {
return xyz;
}
Prototypical chain: Foo -> Function.prototype -> Object.prototype
/*Example 2*/
function Foo () {
return xyz;
}
Foo.prototype = { another:3 }
Prototypical chain: Foo -> Foo.prototype -> Function.prototype -> Object.prototype
function Foo(){
}
Foo.prototype = function(){
}
As per above discussion, the prototypical chain will be Foo -> Function.prototype -> Object.prototype
function Foo(){
this.name = 'bar';
}
Foo.prototype = { }
and the prototypical chain will be Foo -> Object.prototype
For both cases the prototypical chain is valid and inheritance works correctly without any doubt.
Any object can be a prototype. The prototype can be a plain old object, or a function or an array, which are both objects, or anything else which is an object, including regexps and even dates, not to mention objects wrapping primitives such as numbers and strings and booleans.
But when people talk about using, say, a function as a prototype, they usually intend, or hope, that they can somehow access that function from the instantiated object, but they can't. Instances look up properties on the prototype, whether it be an array or a Date or a regexp or a function or a plain old object, but they can't access the "body" of the prototype object, the thing which in the case of objects based on numbers or strings is called PrimitiveValue
.
Here are some examples which might make it clearer, using the simplest case of creating an object based on a prototype with Object.create
.
// plain old object prototype
prototype = { a: 42 };
obj = Object.create(prototype);
obj.a // 42
// array as prototype
array = [ 1, 2 ];
obj = Object.create(array);
array.a = 42
obj.a // 42
// NO WAY TO ACCESS [1, 2] from obj
regexp = /foo/;
regexp.a = 42
obj = Object.create(regexp)
obj.a // 42
// NO WAY TO ACCESS regexp from obj
> number = Object(22)
> number.a = 42
> obj = Object.create(number)
> obj.a // 42
// NO WAY TO ACCESS PRIMITIVE VALUE 22 FROM obj
Now let's consider the case of function as prototype. As with the other types of objects
fn = function() { };
fn.a = 42;
obj = Object.create(fn)
obj.a // 42
// NO WAY TO ACCESS fn from obj
However, since fn
has its own prototype, including methods such as call
, obj
also inherits those. So obj.call
is defined. However, calling it results in an error because obj
is not a function that can be called.
> obj.call()
< Uncaught TypeError: obj is not a function
So the conclusion is that there's nothing particularly wrong or illegal with using a function as a prototype; there's simply no reason to.