March 1999 Draft
JavaScript 2.0
Functions
|
Wednesday, March 24, 1999
To define a function we use the following syntax:
getter
| setter
] function
Identifier (
Parameters )
[TypeExpression] BlockIf Visibility is absent, the above declaration defines a local function within the current Block scope. If Visibility is present, the above declaration declares either a global function (if outside a ClassDefinition's Block) or a class function (if inside a ClassDefinition's Block) according to the declaration scope rules.
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.
Block contains the function body and is evaluated only when the function is called.
A function definition can also be traditional, which is more similar to the behavior of JavaScript 1.x function definitions.
Parameters has one of the following forms:
,
... ,
RequiredParameter [,
OptionalParameter ... ,
OptionalParameter] [,
...
[Identifier]]...
[Identifier]If the ...
is present, the function accepts more arguments than just the listed parameters.
If an Identifier is given after the ...
, then that Identifier
is bound to an array of arguments given after the listed parameters. That Identifier is
declared locally as though by the declaration const array
Identifier.
Individual parameters have the forms:
=
[AssignmentExpression]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 (or undefined
if the AssignmentExpression is missing), 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 ...
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 ...
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.
In addition to local variables generated by the parameters' Identifiers, each function also
has a predefined arguments
local variable which holds an array (of type const array
) of all
arguments passed to this function.
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.
...
to the result.Note that later TypeExpressions and AssignmentExpressions can refer to previously bound arguments. Thus, the following is legal:
function choice(boolean a, type b, b c, b d=) b { return a ? c : d; }
The call choice(true,integer,8,4)
would return 8
, while choice(false,integer,6)
would return
0
(undefined
coerced to type integer
).
Unless the function is a traditional function, the function definition using the above
syntax 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. Any attempt to use this
inside the function's body is an error.
To define a method that can access this
, use the method
keyword.
If a FunctionDefinition is located at a class scope (either because it is located the top
level of a ClassDefinition's Block
or it has a Visibility prefix and is located inside a ClassDefinition's
Block), then the function is a static
method of the class. Unlike C++ or Java, JavaScript 2.0 does not use the static
keyword to indicate such functions;
instead, instance methods (i.e. non-static methods) are defined using the method
keyword.
If a FunctionDefinition contains the keyword getter
or setter
,
then the defined function is a getter or a setter.
A getter must not take any parameters and cannot have a ...
in its Parameters
list. 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 integer x = 0; getter function serialNumber() integer {return ++x} var y = serialNumber; return "<" + serialNumber + "," + serialNumber + "," + y + ">";
A setter must take exactly one required parameter and cannot have a ...
in its Parameters
list. 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 integer x = 0; getter function serialNumber() integer {return ++x} setter function serialNumber(integer n) 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 integer x = 0; function f() integer {return ++x} function g() Function {return f} getter function 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
We can use a getter and a setter to create an alias to another variable, as in:
getter function myAlias() {return Pkg::var} setter function myAlias(x) {return Pkg::var = x} myAlias = myAlias+4;
Traditional function definitions are provided for compatibility with JavaScript 1.x. The syntax is as follows:
traditional
function
Identifier (
Identifier ,
... ,
Identifier )
BlockA function declared with the traditional
keyword cannot have any argument or result
type declarations, optional arguments, or getter
or setter
keyword. Such a function is treated as though every argument were optional and more arguments than just the listed ones were
allowed. Thus, the definition
traditional
function
Identifier (
Identifier ,
... ,
Identifier )
Block
behaves like the following function definition:
function
Identifier (
Identifier =
,
... ,
Identifier =
,
...
)
Block
Furthermore, a traditional function defines its own class and treats this
in the same manner as JavaScript
1.x.
Every function (except a getter or a setter) is also a value and has type Function
. Like other values, it can
be stored in a variable, passed as an argument, and returned as a result. The identifiers in a function are all lexically
scoped.
We can use a variant of a function definition to define a function inside an expression. The syntax is:
function
[Identifier] (
Parameters )
[TypeExpression] BlockThis expression defines a function and returns it as a value of type Function
. The function can be named by
providing the Identifier, but this name is only useful to identify the function in a debugger;
other than that, the function is anonymous.
To avoid confusion between a FunctionDefinition and a FunctionExpression, a Statement (and a few other grammar nonterminals) may not begin with a FunctionExpression. To place a FunctionExpression at the beginning of a Statement, enclose it in parentheses.
A FunctionDefinition is merely convenient syntax for a const
variable definition
and a FunctionExpression:
[Visibility] function
Identifier (
Parameters )
[TypeExpression] Block
is equivalent to:
[Visibility] const
Function
Identifier =
function
Identifier (
Parameters )
[TypeExpression] Block ;
Unless a function is a getter or a setter, we call that function by listing its arguments in parentheses after the function expression, just as in JavaScript 1.x:
(
AssignmentExpression ,
... ,
AssignmentExpression )
An alternative syntax for defining getters and setters would be:
function
[get
| set
] Identifier (
Parameters )
[TypeExpression] BlockThis way we would declare a getter by using, for example,
function get height() integer {...}
instead of:
getter function height() integer {...}
Should we switch to the new syntax?
Do we want to have a named rest parameter (as in the proposal above), or only support the arguments
special local variable as in JavaScript 1.x? The main difference is in the handling of fixed arguments -- they must be added
to the arguments
array but can be omitted from the rest array.
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.x 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 Wednesday, March 24, 1999 |