views:

2230

answers:

4

What is the correct output (meaning correct by the ECMA standard) of the following program?

function nl(x) { document.write(x + "<br>"); }
nl(Function.prototype);
nl(Function.prototype.prototype);
nl(Function.prototype.prototype == Object.prototype);
nl(Function.prototype.prototype.prototype);

Chrome and IE6 agree in saying:

function Empty() {}
null for Chrome / undefined for IE6
false

and then crashing.

Mozilla outputs:

function () { }
[object Object]
false
undefined

Are either of these correct? It seems that the Mozilla one does better, but that the best output is

function () { }
[object Object]
true
undefined
+5  A: 

Function.prototype

From ECMAScript Language Specification:

15.3.3.1 Function.prototype

The initial value of Function.prototype is the Function prototype object (section 15.3.4).

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. The value of the internal [[Prototype]] property of the Function prototype object is the Object prototype object (section 15.3.2.1).

It is a function with an “empty body”; if it is invoked, it merely returns undefined. The Function prototype object does not have a valueOf property of its own; however, it inherits the valueOf property from the Object prototype Object.

I get this output:

  • Opera: function () { [native code] }
  • Chrome: function Empty() {}
  • IE7: function prototype() { [native code]}
  • FF3: function () { }

Chrome and IE7 has named their functions, Opera and IE7 tells you that it will not reveal the implementation. They all agree on this:

nl(typeof Function.prototype); //function

Compare this to:

nl(typeof Object.prototype); //object
nl(typeof Array.prototype); //object
nl(typeof String.prototype); // object

Function.prototype.prototype

I get undefined from Opera and IE7, null from Chrome and [object Object] from FF3. Who is right? Since "The Function prototype object is itself a Function object" shouldn't it be a circular reference to itself? To avoid the circular reference they have chosen different ways. I don't know if there is a standard for that or if it is up to the implementation, but I think an Object is right. Btw, here you see the difference between the internal [[prototype]] and the public prototype in action, like you asked in an earlier question!

Function.prototype.prototype == Object.prototype

This is false because it isn't the same object. See above.

Function.prototype.prototype.prototype

Only FF will give you an answer because of their implementation of Function.prototype.prototype returns an Object.

I agree that your proposed output looks more logic.

They do agree on this:

nl(Object.prototype); // [object Object]
nl(Object.prototype.prototype); // undefined
some
+6  A: 

What you're doing here isn't really walking the prototype chain - this question might help you understand what is actually going on. I didn't bother to check the ECMA spec, but here is my take on the issue:

  • Function is the constructor of function objects

  • Function.prototype is the prototype from which all function objects inherit - it might contain properties like call and apply which are common to all Function instances; the implementations you checked were consistent in that it is implemented as a function object itself (as some pointed out, the ECMA specification requires this)

  • Function.prototype.prototype does't really make much sense, but as Function.prototype is implemented as a function object (which could possibly be used as a constructor), it should at least exists; objects which are created using Function.prototype as constructor would inherit its properties - but as there should be no reason to do something insane like this, setting it to null, undefined or an empty object is reasonable

  • Function.prototype.prototype.prototype will in all likelyhood be undefined: as we have seen before, Function.prototype.prototype should be something without properties (null, undefined or an empty object) and definetely not a function object; therefore, its prototype property should be undefined or might even throw an error when trying to be accessed

Hope this helps ;)

Christoph
Good explanation!
some
+2  A: 
Eugene Lazutkin
I'm a bit confused. Why would the result of the Fun() constructor return something that supported the call() or apply() methods? Those methods belong to functions but what's returned from the constructor is just a plain old object?
Helephant
If you set the prototype to be a function object, then the object created by the constructor would have those properties: function Moo() { }Moo.prototype = function() { };var moo1 = new Moo();alert(moo1.apply)
Helephant
This doesn't work: var moo1 = new ...; moo1(1, "2", true); --- having methods named apply and call doesn't help with operators. JS doesn't allow to define operators and operations, and there is no legal way to inherit/delegate them. I used apply/call as examples that "may" work, but don't.
Eugene Lazutkin
It is easy to dispel the confusion and test your hypothesis --- just run my example and see if it works for you. Then mod it to fit your hypothesis and run again. See where it works (if any). Report back. :-)
Eugene Lazutkin
+1  A: 

I know this post is kind of old, but I've been searching the net for information on this subject and figured I'd post what I found. The prototype property is for constructor functions. It allows you to assign the prototype object of objects you will create with the new keyword.

Every object in JavaScript has a prototype object, but many implementations don't give you direct access to it or allow you to set it after object creation. In FireFox, you can access this object via the "__proto__" property.

Below I have a version of your code using the "__proto__" property. The section on the Function prototype chain matches what you thought it should have been.

function nl(z) { document.write(z + "<br>"); }

x = {};

nl(x["__proto__"]);
nl(x["__proto__"] === Object.prototype);

nl("");

nl(nl.prototype.constructor);
nl(nl["__proto__"].constructor);
nl(nl["__proto__"] === nl.prototype);

nl("");

nl(nl["__proto__"]);
nl(nl["__proto__"] === Function.prototype);
nl(nl["__proto__"]["__proto__"] === Object.prototype);
nl(nl["__proto__"]["__proto__"]["__proto__"]);

nl("");

nl(Function["__proto__"]);
nl(Function["__proto__"]["__proto__"]);
nl(Function["__proto__"]["__proto__"] === Object.prototype);
nl(Function["__proto__"]["__proto__"]["__proto__"]);

Output in FireFox is:

[object Object]
true

function nl(z) { document.write(z + "
"); }
function Function() { [native code] }
false

function () { }
true
true
null

function () { }
[object Object]
true
null
patorjk