Function.prototype.toString spec

# Peter Michaux (18 years ago)

ECMA-262 3rd

== section 15.3.4.2 ==

Function.prototype.toString returns a representation with syntax of FunctionDeclaration

== section13 ==

FunctionDeclaration: function Identifier ( FormalParameterList_opt ) { FunctionBody }

FunctionExpression: function Identifier_opt ( FormalParameterList_opt ) { FunctionBody }

== Firefox results ==

alert((function(){}).toString())

// outputs

function () { }

Note there is no Identifier in this output and an identifier is required in FunctionDeclaration syntax. Should Function.prototype.toString be specified to return a representation with FunctionExpression syntax?

Thanks, Peter

# liorean (18 years ago)

On 02/12/2007, Peter Michaux <petermichaux at gmail.com> wrote:

ECMA-262 3rd == section 15.3.4.2 == Function.prototype.toString returns a representation with syntax of FunctionDeclaration == section13 == FunctionDeclaration: function Identifier ( FormalParameterList_opt ) { FunctionBody } FunctionExpression: function Identifier_opt ( FormalParameterList_opt ) { FunctionBody }

== Firefox results == alert((function(){}).toString()) // outputs function () { }

This is one area where there's some browser incompatibility. To discuss some different syntaces too:

Saf 3.0.4: Function(); // => "function anonymous()\n{\n  ;\n}"
Ie 6 (w/ JScript 5.7): Function(); // => "function anonymous() { \n }"
Kestrel: Function(); // => "function (){}"
Merlin: Function(); // => "function ()\n{\n  }"
Ff2: Function(); //=> "function anonymous() {\n}"
Ff3b1: Function(); //=> "function anonymous() {\n}"

However, if the function has a name "anonymus", you'd expect that variable name to refer to the function object, right? It doesn't, it's a ReferenceError.

Let's do a little more in-detail analysis in browsers:

function literalise(str){
    return '"'+(String(str)
        .replace(/\u005c/g,'\\u005c')
        .replace(/\u000a/g,'\\u000a')
        .replace(/\u000d/g,'\\u000d')
        .replace(/\u0022/g,'\\u0022'))+'"';
}
var
    div=document.createElement('div');
    str='var

u='undefined';prompt('arguments.callee',literalise(typeof arguments===u?u:arguments.callee));prompt('anonymous',literalise(typeof anonymous===u?u:anonymous));prompt('onclick',literalise(typeof onclick===u?u:onclick));', divContents='<button onclick="'+str.replace(/\u0022/g,'"')+'">event</button>', fn=function(){var u='undefined';prompt('arguments.callee',literalise(typeof arguments===u?u:arguments.callee));prompt('anonymous',literalise(typeof anonymous===u?u:anonymous));prompt('onclick',literalise(typeof onclick===u?u:onclick));} window.literalise=literalise; div.innerHTML=divContents; document.documentElement.lastChild.appendChild(div); Function(str)(); setTimeout(str,10); fn();

Merlin/Linear_B: Function(str): argument.callee : "\u000afunction ()\u000a{\u000a var u = \u0022undefined\u0022;\u000a prompt(\u0022arguments.callee\u0022, literalise(typeof arguments === u ? u : arguments.callee));\u000a prompt(\u0022anonymous\u0022, literalise(typeof anonymous === u ? u : anonymous));\u000a prompt(\u0022onclick\u0022, literalise(typeof onclick === u ? u : onclick));\u000a}\u000a" anonymous : "undefined" onclick : "undefined" setTimeout(str,10): argument.callee : "undefined" anonymous : "undefined" onclick : "undefined" fn: argument.callee : "\u000afunction ()\u000a{\u000a var u = \u0022undefined\u0022;\u000a prompt(\u0022arguments.callee\u0022, literalise(typeof arguments === u ? u : arguments.callee));\u000a prompt(\u0022anonymous\u0022, literalise(typeof anonymous === u ? u : anonymous));\u000a prompt(\u0022onclick\u0022, literalise(typeof onclick === u ? u : onclick));\u000a}\u000a" anonymous : "undefined" onclick : "undefined" onclick: argument.callee : "\u000afunction (event)\u000a{\u000a var u = \u0022undefined\u0022;\u000a prompt(\u0022arguments.callee\u0022, literalise(typeof arguments === u ? u : arguments.callee));\u000a prompt(\u0022anonymous\u0022, literalise(typeof anonymous === u ? u : anonymous));\u000a}\u000a" anonymous : "undefined" onclick : "\u000afunction (event)\u000a{\u000a var u = \u0022undefined\u0022;\u000a prompt(\u0022arguments.callee\u0022, literalise(typeof arguments === u ? u : arguments.callee));\u000a prompt(\u0022anonymous\u0022, literalise(typeof anonymous === u ? u : anonymous));\u000a prompt(\u0022onclick\u0022, literalise(typeof onclick === u ? u : onclick));\u000a}\u000a"

Kestrel/Futhark: Function(str): argument.callee : "function (){var u='undefined';prompt('arguments.callee',literalise(typeof arguments===u?u:arguments.callee));prompt('anonymous',literalise(typeof anonymous===u?u:anonymous));prompt('onclick',literalise(typeof onclick===u?u:onclick));}" anonymous : "undefined" onclick : "undefined" setTimeout(str,10): argument.callee : "undefined" anonymous : "undefined" onclick : "undefined" fn: argument.callee : "function(){var u='undefined';prompt('arguments.callee',literalise(typeof arguments===u?u:arguments.callee));prompt('anonymous',literalise(typeof anonymous===u?u:anonymous));prompt('onclick',literalise(typeof onclick===u?u:onclick));}" anonymous : "undefined" onclick : "undefined" onclick: argument.callee : "var u='undefined';prompt('arguments.callee',literalise(typeof arguments===u?u:arguments.callee));prompt('anonymous',literalise(typeof anonymous===u?u:anonymous));prompt('onclick',literalise(typeof onclick===u?u:onclick));" anonymous : "undefined" onclick : "var u='undefined';prompt('arguments.callee',literalise(typeof arguments===u?u:arguments.callee));prompt('anonymous',literalise(typeof anonymous===u?u:anonymous));prompt('onclick',literalise(typeof onclick===u?u:onclick));"

Ie6/JScript5.7: Function(str): argument.callee : "function anonymous() {\u000avar u='undefined';prompt('arguments.callee',literalise(typeof arguments===u?u:arguments.callee));prompt('anonymous',literalise(typeof anonymous===u?u:anonymous));prompt('onclick',literalise(typeof onclick===u?u:onclick));\u000a}" anonymous : "undefined" onclick : "undefined" setTimeout(str,10): argument.callee : "function anonymous()\u000a{\u000avar u='undefined';prompt('arguments.callee',literalise(typeof arguments===u?u:arguments.callee));prompt('anonymous',literalise(typeof anonymous===u?u:anonymous));prompt('onclick',literalise(typeof onclick===u?u:onclick));\u000a}" anonymous : "undefined" onclick : "undefined" fn: argument.callee : "function(){var u='undefined';prompt('arguments.callee',literalise(typeof arguments===u?u:arguments.callee));prompt('anonymous',literalise(typeof anonymous===u?u:anonymous));prompt('onclick',literalise(typeof onclick===u?u:onclick));}" anonymous : "undefined" onclick : "undefined" onclick: argument.callee : "function anonymous()\u000a{\u000avar u='undefined';prompt('arguments.callee',literalise(typeof arguments===u?u:arguments.callee));prompt('anonymous',literalise(typeof anonymous===u?u:anonymous));prompt('onclick',literalise(typeof onclick===u?u:onclick));\u000a}" anonymous : "undefined" onclick : "function anonymous()\u000a{\u000avar u='undefined';prompt('arguments.callee',literalise(typeof arguments===u?u:arguments.callee));prompt('anonymous',literalise(typeof anonymous===u?u:anonymous));prompt('onclick',literalise(typeof onclick===u?u:onclick));\u000a}"

Saf3.0.4b/JavaScriptCore: Function(str): argument.callee: "function anonymous() \u000a{\u000a var u = \u0022undefined\u0022;\u000a prompt(\u0022arguments.callee\u0022, literalise(typeof arguments === u ? u : arguments.callee));\u000a prompt(\u0022anonymous\u0022, literalise(typeof anonymous === u ? u : anonymous));\u000a prompt(\u0022onclick\u0022, literalise(typeof onclick === u ? u : onclick));\u000a}" anonymous: "undefined" onclick: "null" setTimeout(str,10): (Broken? Had to change the prompt to alert to display these...) argument.callee: "undefined" anonymous: "undefined" onclick: "null" fn: argument.callee : "function () \u000a{\u000a var u = \u0022undefined\u0022;\u000a prompt(\u0022arguments.callee\u0022, literalise(typeof arguments === u ? u : arguments.callee));\u000a prompt(\u0022anonymous\u0022, literalise(typeof anonymous === u ? u : anonymous));\u000a prompt(\u0022onclick\u0022, literalise(typeof onclick === u ? u : onclick));\u000a}" anonymous : "undefined" onclick : "null" onclick: argument.callee: "function onclick(event) \u000a{\u000a var u = \u0022undefined\u0022;\u000a prompt(\u0022arguments.callee\u0022, literalise(typeof arguments === u ? u : arguments.callee));\u000a prompt(\u0022anonymous\u0022, literalise(typeof anonymous === u ? u : anonymous));\u000a prompt(\u0022onclick\u0022, literalise(typeof onclick === u ? u : onclick));\u000a}" anonymous: "undefined" onclick: "function onclick(event) \u000a{\u000a var u = \u0022undefined\u0022;\u000a prompt(\u0022arguments.callee\u0022, literalise(typeof arguments === u ? u : arguments.callee));\u000a prompt(\u0022anonymous\u0022, literalise(typeof anonymous === u ? u : anonymous));\u000a prompt(\u0022onclick\u0022, literalise(typeof onclick === u ? u : onclick));\u000a}"

Ff3.0b1/SpiderMonkey: Function(str): argument.callee: "function anonymous() {\u000a var u = \u0022undefined\u0022;\u000a prompt(\u0022arguments.callee\u0022, literalise(typeof arguments === u ? u : arguments.callee));\u000a prompt(\u0022anonymous\u0022, literalise(typeof anonymous === u ? u : anonymous));\u000a prompt(\u0022onclick\u0022, literalise(typeof onclick === u ? u : onclick));\u000a}" anonymous: "undefined" onclick: "undefined" setTimeout(str,10): argument.callee: "undefined" anonymous: "undefined" onclick: "undefined" fn: argument.callee : "function () {\u000a var u = \u0022undefined\u0022;\u000a prompt(\u0022arguments.callee\u0022, literalise(typeof arguments === u ? u : arguments.callee));\u000a prompt(\u0022anonymous\u0022, literalise(typeof anonymous === u ? u : anonymous));\u000a prompt(\u0022onclick\u0022, literalise(typeof onclick === u ? u : onclick));\u000a}" anonymous : "undefined" onclick : "undefined" onclick: argument.callee: "function onclick(event) {\u000a var u = \u0022undefined\u0022;\u000a prompt(\u0022arguments.callee\u0022, literalise(typeof arguments === u ? u : arguments.callee));\u000a prompt(\u0022anonymous\u0022, literalise(typeof anonymous === u ? u : anonymous));\u000a prompt(\u0022onclick\u0022, literalise(typeof onclick === u ? u : onclick));\u000a}" anonymous: "undefined" onclick: "function onclick(event) {\u000a var u = \u0022undefined\u0022;\u000a prompt(\u0022arguments.callee\u0022, literalise(typeof arguments === u ? u : arguments.callee));\u000a prompt(\u0022anonymous\u0022, literalise(typeof anonymous === u ? u : anonymous));\u000a prompt(\u0022onclick\u0022, literalise(typeof onclick === u ? u : onclick));\u000a}"

Ff2.0.0.11/SpiderMonkey: Function(str): argument.callee: "function anonymous() {\u000a var u = \u0022undefined\u0022;\u000a prompt(\u0022arguments.callee\u0022, literalise(typeof arguments === u ? u : arguments.callee));\u000a prompt(\u0022anonymous\u0022, literalise(typeof anonymous === u ? u : anonymous));\u000a prompt(\u0022onclick\u0022, literalise(typeof onclick === u ? u : onclick));\u000a}" anonymous: "undefined" onclick: "undefined" setTimeout(str,10): argument.callee: "undefined" anonymous: "undefined" onclick: "undefined" fn: argument.callee : "function () {\u000a var u = \u0022undefined\u0022;\u000a prompt(\u0022arguments.callee\u0022, literalise(typeof arguments === u ? u : arguments.callee));\u000a prompt(\u0022anonymous\u0022, literalise(typeof anonymous === u ? u : anonymous));\u000a prompt(\u0022onclick\u0022, literalise(typeof onclick === u ? u : onclick));\u000a}" anonymous : "undefined" onclick : "undefined" onclick: argument.callee: "function onclick(event) {\u000a var u = \u0022undefined\u0022;\u000a prompt(\u0022arguments.callee\u0022, literalise(typeof arguments === u ? u : arguments.callee));\u000a prompt(\u0022anonymous\u0022, literalise(typeof anonymous === u ? u : anonymous));\u000a prompt(\u0022onclick\u0022, literalise(typeof onclick === u ? u : onclick));\u000a}" anonymous: "undefined" onclick: "function onclick(event) {\u000a var u = \u0022undefined\u0022;\u000a prompt(\u0022arguments.callee\u0022, literalise(typeof arguments === u ? u : arguments.callee));\u000a prompt(\u0022anonymous\u0022, literalise(typeof anonymous === u ? u : anonymous));\u000a prompt(\u0022onclick\u0022, literalise(typeof onclick === u ? u : onclick));\u000a}"

Note there is no Identifier in this output and an identifier is required in FunctionDeclaration syntax. Should Function.prototype.toString be specified to return a representation with FunctionExpression syntax?

IMO, if the serialisation includes a name, that name should be usable as a variable reference within it. So the serialisation should never include a name if it's not there.

However, this simple rule falls apart for several reasons, most important that function declarations are bound in the outside scope only, and not on the inside scope at all. This means a dissociated function object from a function declaration will no longer be able to use the original name as a reference to itself.

# Lars T Hansen (18 years ago)

Aware of it. This has been resolved in favor of making the spec say (effectively) "FunctionDeclaration or FunctionExpression".