July 2000 Draft
JavaScript 2.0
Core Language
Functions
|
Friday, May 26, 2000
A function definition not preceded by the abstract
attribute and not followed by a Block
is called a function declaration. A function declaration indicates that the function will be defined later in the same scope.
A function declaration's signature must exactly match the eventual definition's.
The Block contains the function body and is evaluated only when the function is called.
Like other definitions and declarations, a function definition or declaration may be preceded
by one or more attributes, which affect the function's scope, namespace, and semantics.
Every function (except a getter or a setter) is also a value and has type Function
.
If a FunctionDeclaration is introduced with the function
keyword, then it defines a function, getter (if FunctionName
begins with get
), or setter (if FunctionName
begins with set
). If the FunctionDeclaration
begins with the constructor
keyword, then it defines a class
constructor. It is customary to give a class constructor the same name as its class.
A FunctionSignature gives the names and the types of the function's parameters and result. A function may take zero or more required parameters, followed by zero or more optional parameters, optionally followed by a rest parameter.
Individual parameters have the forms:
The TypeExpression gives the parameter's type
and defaults to type any
. If the parameter name Identifier
is followed by a =
, then that parameter is optional. If the nth
parameter is optional and a call to this function provides fewer than n arguments, then the nth
parameter is set to the value of its AssignmentExpression,
coerced to the nth parameter's type if necessary. The nth parameter's AssignmentExpression
is evaluated only if fewer than n arguments are given in a call.
A RequiredParameter may not follow an OptionalParameter. If a function has n RequiredParameters and m OptionalParameters and no RestParameter in its parameter list, then any call of that function must supply at least n arguments and at most n+m arguments. If this function has a RestParameter in its parameter list, then any call of that function must supply at least n arguments. These restrictions do not apply to traditional functions.
The parameters' Identifiers are local variables with types given by the corresponding TypeExpressions inside the function's Block. Code in the Block may read and write these variables. Arguments are passed by value, so writes to these variables do not affect the passed arguments' values in the caller.
If the ...
is present, the function accepts more arguments than just the listed required
and optional parameters. If a TypedIdentifier is given after
the ...
, then that TypedIdentifier's
Identifier is bound to an array of arguments given
after the listed parameters. Named arguments are passed as named properties
of that array. That Identifier is declared as a local
const
using the TypedIdentifier's TypeExpression
type, or Array
if not provided.
If a named argument is passed to a function call, then that argument and all subsequent ones can only match a RestParameter.
In non-strict mode, each function also has a predefined
const
arguments
local variable which holds an array (of type Array
) of all arguments
passed to this function.
The function's result type is TypeExpression,
which defaults to type any
if not given. If the function does not return a value, it's good practice to set TypeExpression
to void
to document this fact.
A function's parameter and result TypeExpressions are evaluated at the time the function definition or declaration is executed. These types are then saved for use in argument and result coercions at the time the function is called.
The static and dynamic extent of a parameter includes all subsequent parameters' and the result type's TypeExpressions and AssignmentExpressions. However, the case where a subsequent parameter's or the result type's TypeExpression references a prior parameter is reserved for a future language extension. For now, an implementation must detect this case and raise an error:
const t = Integer; function choice(a:Boolean, t:Type, c:t, d:t):t { return a ? c : d; }
This definition of choice
should (for now) be an error and not:
function choice(a:Boolean, t:Type, c:Integer, d:Integer):Integer { return a ? c : d; }
The intent is that a future language extension might make the first definition of choice
legal and permit
calls to it like choice(true,String,"Be","Not Be")
, which would return "Be"
.
On the other hand, an optional or rest parameter's AssignmentExpression may refer to the values of prior parameters:
function f(a:Integer, b:Integer = a, c:Integer = b):Integer { return a + b + c; } f(3); // Returns 9 f(3, 4); // Returns 11 f(3, 4, 10); // Returns 17
When a function is called, the following list indicates the order of evaluation of the various expressions in a FunctionDefinition. These steps are taken only after all of the arguments have been evaluated.
If a FunctionName contains the keyword get
or set
,
then the defined function is a getter or a setter.
A getter must not take any parameters. Unlike an ordinary function, a getter is invoked by merely
mentioning its name without an Arguments list in any expression
except as the destination of an assignment. For example, the following code returns the string “<2,3,1>
”:
var x:Integer = 0; function get serialNumber():Integer {return ++x} var y = serialNumber; return "<" + serialNumber + "," + serialNumber + "," + y + ">";
A setter must take exactly one required parameter. Unlike an ordinary function, a setter is invoked
by merely mentioning its name (without an Arguments list) on
the left side of an assignment or as the target of a mutator such as ++
or --
. The result of the
setter becomes the result of the assignment. For example, the following code returns the string “<1,2,43>
”:
var x:Integer = 0; function get serialNumber():Integer {return ++x} function set serialNumber(n:Integer):Integer {return x=n} var s = "<" + serialNumber + "," + serialNumber; serialNumber = 42; return s + "," + serialNumber + ">";
A setter can have the same name as a getter in the same lexical scope. A getter or setter cannot be extracted from its variable, so the notion of the type of a getter or setter is vacuous; a getter or setter can only be called.
Contrast the following:
var x:Integer = 0; function f():Integer {return ++x} function g():Function {return f} function get h():Function {return f} f; // Evaluates to function f g; // Evaluates to function g h; // Evaluates to function f (not h) f(); // Evaluates to 1 g(); // Evaluates to function f h(); // Evaluates to 2 g()(); // Evaluates to 3
A traditional function is specified by preceding the function definition with the traditional
attribute. Traditional function definitions are provided for compatibility with JavaScript 1.5.
A traditional function defines its own class and treats this
in the same manner as JavaScript 1.5. A non-traditional
function does not define a class; the function's name cannot be used in a new
expression, and the function
does not have a this
parameter unless it is a non-static member of a class or interface.
By consensus in the ECMA TC39 modularity subcommittee, we decided to use the above syntax for getters and setters instead of:
getter
| setter
] function
Identifier (
Parameters )
[:
TypeExpression] BlockThe decision was based on aesthetics; neither syntax is more difficult to implement than the other.
The traditional
keyword is ugly, so let's take a look at some alternatives. Unless we want to continue to
make each function into a class (as JavaScript 1.5 does), we need some way to indicate which functions are also classes
and which ones are not. Also, we'd like to be able to indicate which functions can be called with more or fewer than the
desired number of arguments and which cannot.
One possibility would be to state that any function that uses a type annotation in its signature (either the parameter
list or the result type) is a new-style function and does not define a class; other functions would declare classes. Furthermore,
new-style functions would have to be called with the exact number of arguments unless some parameters are optional or a
...
is present in the parameter list. These are analogous to the rules that ANSI C used to distinguish new-style
functions from traditional C functions. As with ANSI C, we have somewhat of a difficulty with functions that take no parameters;
such functions would need to specify a return type to be considered new-style.
C++ did away with the ANSI C treatment of traditional C functions. We could do the same by having a pragma (analogous
to Perl's use
pragmas) that could indicate that all functions are to be considered new-style unless prefixed
by the traditional
keyword. If we do this, we should decide whether the default setting of this pragma would
be on or off.
Waldemar Horwat Last modified Friday, May 26, 2000 |