8

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?

Alexander Mills
  • 90,741
  • 139
  • 482
  • 817
  • 4
    Why not try it and see? – Alex McMillan May 24 '15 at 04:59
  • 2
    you cannot use the function with prototype keyword. – Mert Metin May 24 '15 at 05:01
  • It's wrong because prototypes are objects, but what are you expecting to do inside that function? – Andy Ray May 24 '15 at 05:02
  • well in js functions are objects etc..it would make sense to chain constructor functions as other languages do – Alexander Mills May 24 '15 at 05:04
  • 3
    @AndyRay - but functions are objects, so why is it wrong? (Not that I would suggest actually doing it. But it works.) – nnnnnn May 24 '15 at 05:04
  • The first is correct, you can use a `function` to declare a class, and the function itself is the constructor. Assigning a value to `Foo.prototype` however doesn't make sense in any of the cases. You have to use `Foo.prototype.methodName = function () ...`, and yes, you have to use `function()` because you are declaring a function, the second case is instancing an empty object. – Havenard May 24 '15 at 05:10
  • 1
    Also, all value assignment operations should be closed by `;`, even if the value is a function. JavaScript is robust enough to understand an implicit `;` in most cases, but this is irregular. – Havenard May 24 '15 at 05:12
  • It doesn't make any sense - which is why I'm asking what you want to do inside that function? The body will never execute. It will technically make your instantiated object `new Foo()` inherit from the `Function` (capital F) prototype, which is confusing. If you want instances of `Foo` to inherit from `Function` you should use the correct syntax of `Foo.prototype = Object.Create(Function.prototype)`, but you probably don't want that? – Andy Ray May 24 '15 at 05:12
  • @Havenard I don't think you're right. Assigning Foo.prototype = {} is meaningful..for example,Foo.prototype = { baz:'egghead'} is the same as Foo.prototype.baz = 'egghead'. Seen? – Alexander Mills May 24 '15 at 05:37
  • 1
    @AlexMills: [Not exactly](https://stackoverflow.com/questions/17474390/defining-a-javascript-prototype), no. – Bergi May 24 '15 at 08:35
  • There is an extensive answer on this topic lurking somewhere on SO that I can't track down right now, maybe someone else can. –  May 24 '15 at 14:36
  • @torazaburo: I think there is an extensive thread about subclassing `Function`, but none about using function objects for prototypes. – Bergi May 24 '15 at 16:24

2 Answers2

6

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.

Object.prototype vs Function Prototype

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

For option 1:

function Foo(){

}

Foo.prototype = function(){

}

As per above discussion, the prototypical chain will be Foo -> Function.prototype -> Object.prototype

For option 2:

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.

Scimonster
  • 32,893
  • 9
  • 77
  • 89
Samir Das
  • 1,878
  • 12
  • 20
  • What do you mean by "*loss [of] its prototypical inheritance nature*"?! `foo.getName` is definitely available, there is nothing that is "reset". – Bergi May 24 '15 at 08:37
  • Of course, assigning a `.name` property on an object that does inherit from a function does pose some difficulties. But this just shows that it does indeed inherit! – Bergi May 24 '15 at 08:39
  • @Bergi I have updated answer for better understanding. Now please explain how `foo.getName` is available for the first case – Samir Das May 24 '15 at 09:20
  • Well, "*`foo.getName()` is not available as you see it*" is still wrong. You shouldn't trust too much what you see. If `console.log` views a function object as a function literal, that doesn't mean the object would not have any properties. As you can call it and it returns a string without an error, it obviously *is* available. – Bergi May 24 '15 at 09:53
  • I read this through a couple of times but have no idea what you are saying. –  May 24 '15 at 16:56
  • @Bergi you are right about. I have revised answer. Good night. Thank you – Samir Das May 24 '15 at 20:45
3

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.