ES2019: Function.prototype.toString revision

[2016-08-31] dev, javascript, es2019
(Ad, please don’t block)

The ECMAScript proposal “Function.prototype.toString revision” (by Michael Ficarra) is at stage 4 and therefore part of ECMAScript 2019. It brings two major improvements compared to ES2016:

  • Whenever possible – source code: If a function was created via ECMAScript source code, toString() must return that source code. In ES2016, whether to do so is left up to engines.
  • Otherwise – standardized placeholder: In ES2016, if toString() could not (or would not) create syntactically valid ECMAScript code, it had to return a string for which eval() throws a SyntaxError. In other words, eval() must not be able to parse the string. This requirement was forward-incompatible – whatever string you come up with, you can never be completely sure that a future version of ECMAScript doesn’t make it syntactically valid. In contrast, the proposal standardizes a placeholder: a function whose body is { [native code] }. Details are explained in the next section.

The algorithm  

The proposal distinguishes:

  • Functions defined via ECMAScript code: toString() must return their original source code.

    toString() may return code that is only syntactically valid within its syntactic context:

    > class C { foo() { /*hello*/ } }
    > C.prototype.foo.toString()
    'foo() { /*hello*/ }'
    

    The following two kinds of line breaks are converted to Unix-style '\n':

    • Windows: '\r\n'
    • Classic macOS: '\r'
  • Built-in function objects, bound function exotic objects and callable objects which were not defined via ECMAScript code: toString() must return a so-called NativeFunction string, which looks as follows.

    "function" BindingIdentifier? "(" FormalParameters ")"
    "{ [native code] }"
    

    The parameters can be omitted. If the function is a “well-known intrinsic object” (such as Array, Error, isNaN, etc.) then the initial value of its name property must appear in the result. Examples:

    > isNaN.toString()
    'function isNaN() { [native code] }'
    > Math.pow.toString()
    'function pow() { [native code] }'
    > (function foo() {}).bind(null).toString()
    'function () { [native code] }'
    
  • Functions created dynamically via the constructors Function and GeneratorFunction: engines must create the appropriate source code and attach it to the functions. This source code is then returned by toString().

  • In all other cases (the receiver this is not callable): throw a TypeError.