Function.prototype.toString spec

liorean liorean at gmail.com
Sat Dec 1 21:44:35 PST 2007


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,'&#x0022;')+'">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.
-- 
David "liorean" Andersson



More information about the Es4-discuss mailing list